1 /**
2 
3 This module defines facilities for efficient checking of integral operations
4 against overflow, casting with loss of precision, unexpected change of sign,
5 etc. The checking (and possibly correction) can be done at operation level, for
6 example $(LREF opChecked)$(D !"+"(x, y, overflow)) adds two integrals `x` and
7 `y` and sets `overflow` to `true` if an overflow occurred. The flag `overflow`
8 (a `bool` passed by reference) is not touched if the operation succeeded, so the
9 same flag can be reused for a sequence of operations and tested at the end.
10 
11 Issuing individual checked operations is flexible and efficient but often
12 tedious. The $(LREF Checked) facility offers encapsulated integral wrappers that
13 do all checking internally and have configurable behavior upon erroneous
14 results. For example, `Checked!int` is a type that behaves like `int` but aborts
15 execution immediately whenever involved in an operation that produces the
16 arithmetically wrong result. The accompanying convenience function $(LREF
17 checked) uses type deduction to convert a value `x` of integral type `T` to
18 `Checked!T` by means of `checked(x)`. For example, $(D checked(1_000_000) *
19 10_000) aborts execution because the operation overflows. Also, $(D checked(-1) >
20 uint(0)) aborts execution (even though the built-in comparison $(D int(-1) >
21 uint(0)) is surprisingly true due to language's conversion rules modeled  after
22 C). Thus, `Checked!int` is a virtually drop-in replacement for `int` useable in
23 debug builds, to be replaced by `int` in release mode if efficiency demands it.
24 
25 `Checked`  has customizable behavior with the help of a second type parameter,
26 `Hook`. Depending on what methods `Hook` defines, core operations on the
27 underlying integral may be verified for overflow or completely redefined. If
28 `Hook` defines no method at all and carries no state, there is no change in
29 behavior, i.e. $(D Checked!(int, void)) is a wrapper around `int` that adds no
30 customization at all.
31 
32 This module provides a few predefined hooks (below) that add useful behavior to
33 `Checked`:
34 
35 $(UL
36 $(LI $(LREF Abort) fails every incorrect operation with a message to $(REF
37 stderr, std, stdio) followed by a call to `assert(0)`. It is the default
38 second parameter, i.e. `Checked!short` is the same as $(D Checked!(short,
39 Abort)).
40 )
41 $(LI $(LREF Warn) prints incorrect operations to $(REF stderr, std, stdio) but
42 otherwise preserves the built-in behavior.
43 )
44 $(LI $(LREF ProperCompare) fixes the comparison operators `==`, `!=`, `<`, `<=`, `>`,
45 and `>=` to return correct results in all circumstances, at a slight cost in
46     efficiency. For example, $(D Checked!(uint, ProperCompare)(1) > -1) is `true`,
47 which is not the case for the built-in comparison. Also, comparing numbers for
48 equality with floating-point numbers only passes if the integral can be
49 converted to the floating-point number precisely, so as to preserve transitivity
50 of equality.
51 )
52 $(LI $(LREF WithNaN) reserves a special "Not a Number" value akin to the homonym
53 value reserved for floating-point values. Once a $(D Checked!(X, WithNaN)) gets
54 this special value, it preserves and propagates it until reassigned.
55 )
56 $(LI $(LREF Saturate) implements saturating arithmetic, i.e. $(D Checked!(int,
57 Saturate)) "stops" at `int.max` for all operations that would cause an `int` to
58 overflow toward infinity, and at `int.min` for all operations that would
59 correspondingly overflow toward negative infinity.
60 )
61 $(LI $(LREF Throw), like $(D Abort), immediately fails on a bounds error, but it
62 throws a CheckFailure exception in place of `assert(0)`.
63 )
64 )
65 
66 These policies may be used alone, e.g. $(D Checked!(uint, WithNaN)) defines a
67 `uint`-like type that reaches a stable NaN state for all erroneous operations.
68 They may also be "stacked" on top of each other, owing to the property that a
69 checked integral emulates an actual integral, which means another checked
70 integral can be built on top of it. Some combinations of interest include:
71 
72 $(UL
73 $(LI $(D Checked!(Checked!int, ProperCompare)) defines an `int` with fixed
74 comparison operators that will fail with `assert(0)` upon overflow. (Recall that
75 `Abort` is the default policy.) The order in which policies are combined is
76 important because the outermost policy (`ProperCompare` in this case) has the
77 first crack at intercepting an operator. The converse combination $(D
78 Checked!(Checked!(int, ProperCompare))) is meaningless because `Abort` will
79 intercept comparison and will fail without giving `ProperCompare` a chance to
80 intervene.
81 )
82 $(LI $(D Checked!(Checked!(int, ProperCompare), WithNaN)) defines an `int`-like
83 type that supports a NaN value. For values that are not NaN, comparison works
84 properly. Again the composition order is important; $(D Checked!(Checked!(int,
85 WithNaN), ProperCompare)) does not have good semantics because `ProperCompare`
86 intercepts comparisons before the numbers involved are tested for NaN.
87 )
88 )
89 
90 The hook's members are looked up statically in a Design by Introspection manner
91 and are all optional. The table below illustrates the members that a hook type
92 may define and their influence over the behavior of the `Checked` type using it.
93 In the table, `hook` is an alias for `Hook` if the type `Hook` does not
94 introduce any state, or an object of type `Hook` otherwise.
95 
96 $(TABLE ,
97 $(TR $(TH `Hook` member) $(TH Semantics in $(D Checked!(T, Hook)))
98 )
99 $(TR $(TD `defaultValue`) $(TD If defined, `Hook.defaultValue!T` is used as the
100 default initializer of the payload.)
101 )
102 $(TR $(TD `min`) $(TD If defined, `Hook.min!T` is used as the minimum value of
103 the payload.)
104 )
105 $(TR $(TD `max`) $(TD If defined, `Hook.max!T` is used as the maximum value of
106 the payload.)
107 )
108 $(TR $(TD `hookOpCast`) $(TD If defined, `hook.hookOpCast!U(get)` is forwarded
109 to unconditionally when the payload is to be cast to type `U`.)
110 )
111 $(TR $(TD `onBadCast`) $(TD If defined and `hookOpCast` is $(I not) defined,
112 `onBadCast!U(get)` is forwarded to when the payload is to be cast to type `U`
113 and the cast would lose information or force a change of sign.)
114 )
115 $(TR $(TD `hookOpEquals`) $(TD If defined, $(D hook.hookOpEquals(get, rhs)) is
116 forwarded to unconditionally when the payload is compared for equality against
117 value `rhs` of integral, floating point, or Boolean type.)
118 )
119 $(TR $(TD `hookOpCmp`) $(TD If defined, $(D hook.hookOpCmp(get, rhs)) is
120 forwarded to unconditionally when the payload is compared for ordering against
121 value `rhs` of integral, floating point, or Boolean type.)
122 )
123 $(TR $(TD `hookOpUnary`) $(TD If defined, `hook.hookOpUnary!op(get)` (where `op`
124 is the operator symbol) is forwarded to for unary operators `-` and `~`. In
125 addition, for unary operators `++` and `--`, `hook.hookOpUnary!op(payload)` is
126 called, where `payload` is a reference to the value wrapped by `Checked` so the
127 hook can change it.)
128 )
129 $(TR $(TD `hookOpBinary`) $(TD If defined, $(D hook.hookOpBinary!op(get, rhs))
130 (where `op` is the operator symbol and `rhs` is the right-hand side operand) is
131 forwarded to unconditionally for binary operators `+`,  `-`, `*`, `/`, `%`,
132 `^^`, `&`, `|`, `^`, `<<`, `>>`, and `>>>`.)
133 )
134 $(TR $(TD `hookOpBinaryRight`) $(TD If defined, $(D
135 hook.hookOpBinaryRight!op(lhs, get)) (where `op` is the operator symbol and
136 `lhs` is the left-hand side operand) is forwarded to unconditionally for binary
137 operators `+`,  `-`, `*`, `/`, `%`, `^^`, `&`, `|`, `^`, `<<`, `>>`, and `>>>`.)
138 )
139 $(TR $(TD `onOverflow`) $(TD If defined, `hook.onOverflow!op(get)` is forwarded
140 to for unary operators that overflow but only if `hookOpUnary` is not defined.
141 Unary `~` does not overflow; unary `-` overflows only when the most negative
142 value of a signed type is negated, and the result of the hook call is returned.
143 When the increment or decrement operators overflow, the payload is assigned the
144 result of `hook.onOverflow!op(get)`. When a binary operator overflows, the
145 result of $(D hook.onOverflow!op(get, rhs)) is returned, but only if `Hook` does
146 not define `hookOpBinary`.)
147 )
148 $(TR $(TD `hookOpOpAssign`) $(TD If defined, $(D hook.hookOpOpAssign!op(payload,
149 rhs)) (where `op` is the operator symbol and `rhs` is the right-hand side
150 operand) is forwarded to unconditionally for binary operators `+=`,  `-=`, `*=`, `/=`, `%=`,
151 `^^=`, `&=`, `|=`, `^=`, `<<=`, `>>=`, and `>>>=`.)
152 )
153 $(TR $(TD `onLowerBound`) $(TD If defined, $(D hook.onLowerBound(value, bound))
154 (where `value` is the value being assigned) is forwarded to when the result of
155 binary operators `+=`,  `-=`, `*=`, `/=`, `%=`, `^^=`, `&=`, `|=`, `^=`, `<<=`, `>>=`,
156 and `>>>=` is less than the minimum value representable by `T`.)
157 )
158 $(TR $(TD `onUpperBound`) $(TD If defined, $(D hook.onUpperBound(value, bound))
159 (where `value` is the value being assigned) is forwarded to when the result of
160 binary operators `+=`,  `-=`, `*=`, `/=`, `%=`, `^^=`, `&=`, `|=`, `^=`, `<<=`, `>>=`,
161 and `>>>=` is greater than the maximum value representable by `T`.)
162 )
163 )
164 
165 */
166 module std.experimental.checkedint;
167 import std.traits : isFloatingPoint, isIntegral, isNumeric, isUnsigned, Unqual;
168 
169 ///
170 unittest
171 {
172     int[] concatAndAdd(int[] a, int[] b, int offset)
173     {
174         // Aborts on overflow on size computation
175         auto r = new int[(checked(a.length) + b.length).get];
176         // Aborts on overflow on element computation
177         foreach (i; 0 .. a.length)
178             r[i] = (a[i] + checked(offset)).get;
179         foreach (i; 0 .. b.length)
180             r[i + a.length] = (b[i] + checked(offset)).get;
181         return r;
182     }
183     assert(concatAndAdd([1, 2, 3], [4, 5], -1) == [0, 1, 2, 3, 4]);
184 }
185 
186 /**
187 Checked integral type wraps an integral `T` and customizes its behavior with the
188 help of a `Hook` type. The type wrapped must be one of the predefined integrals
189 (unqualified), or another instance of `Checked`.
190 */
191 struct Checked(T, Hook = Abort)
192 if (isIntegral!T || is(T == Checked!(U, H), U, H))
193 {
194     import std.algorithm.comparison : among;
195     import std.traits : hasMember;
196     import std.experimental.allocator.common : stateSize;
197 
198     /**
199     The type of the integral subject to checking.
200     */
201     alias Representation = T;
202 
203     // state {
204     static if (hasMember!(Hook, "defaultValue"))
205         private T payload = Hook.defaultValue!T;
206     else
207         private T payload;
208     /**
209     `hook` is a member variable if it has state, or an alias for `Hook`
210     otherwise.
211     */
212     static if (stateSize!Hook > 0) Hook hook;
213     else alias hook = Hook;
214     // } state
215 
216     // get
217     /**
218     Returns a copy of the underlying value.
219     */
220     auto get() inout { return payload; }
221     ///
222     unittest
223     {
224         auto x = checked(ubyte(42));
225         static assert(is(typeof(x.get()) == ubyte));
226         assert(x.get == 42);
227         const y = checked(ubyte(42));
228         static assert(is(typeof(y.get()) == const ubyte));
229         assert(y.get == 42);
230     }
231 
232     /**
233     Defines the minimum and maximum. These values are hookable by defining
234     `Hook.min` and/or `Hook.max`.
235     */
236     static if (hasMember!(Hook, "min"))
237     {
238         enum Checked!(T, Hook) min = Checked!(T, Hook)(Hook.min!T);
239         ///
240         unittest
241         {
242             assert(Checked!short.min == -32768);
243             assert(Checked!(short, WithNaN).min == -32767);
244             assert(Checked!(uint, WithNaN).max == uint.max - 1);
245         }
246     }
247     else
248         enum Checked!(T, Hook) min = Checked(T.min);
249     /// ditto
250     static if (hasMember!(Hook, "max"))
251         enum Checked!(T, Hook) max = Checked(Hook.max!T);
252     else
253         enum Checked!(T, Hook) max = Checked(T.max);
254 
255     /**
256     Constructor taking a value properly convertible to the underlying type. `U`
257     may be either an integral that can be converted to `T` without a loss, or
258     another `Checked` instance whose representation may be in turn converted to
259     `T` without a loss.
260     */
261     this(U)(U rhs)
262     if (valueConvertible!(U, T) ||
263         !isIntegral!T && is(typeof(T(rhs))) ||
264         is(U == Checked!(V, W), V, W) &&
265             is(typeof(Checked!(T, Hook)(rhs.get))))
266     {
267         static if (isIntegral!U)
268             payload = rhs;
269         else
270             payload = rhs.payload;
271     }
272     ///
273     unittest
274     {
275         auto a = checked(42L);
276         assert(a == 42);
277         auto b = Checked!long(4242); // convert 4242 to long
278         assert(b == 4242);
279     }
280 
281     /**
282     Assignment operator. Has the same constraints as the constructor.
283     */
284     void opAssign(U)(U rhs) if (is(typeof(Checked!(T, Hook)(rhs))))
285     {
286         static if (isIntegral!U)
287             payload = rhs;
288         else
289             payload = rhs.payload;
290     }
291     ///
292     unittest
293     {
294         Checked!long a;
295         a = 42L;
296         assert(a == 42);
297         a = 4242;
298         assert(a == 4242);
299     }
300 
301     // opCast
302     /**
303     Casting operator to integral, `bool`, or floating point type. If `Hook`
304     defines `hookOpCast`, the call immediately returns
305     `hook.hookOpCast!U(get)`. Otherwise, casting to `bool` yields $(D
306     get != 0) and casting to another integral that can represent all
307     values of `T` returns `get` promoted to `U`.
308 
309     If a cast to a floating-point type is requested and `Hook` defines
310     `onBadCast`, the cast is verified by ensuring $(D get == cast(T)
311     U(get)). If that is not `true`, `hook.onBadCast!U(get)` is returned.
312 
313     If a cast to an integral type is requested and `Hook` defines `onBadCast`,
314     the cast is verified by ensuring `get` and $(D cast(U)
315     get) are the same arithmetic number. (Note that `int(-1)` and
316     `uint(1)` are different values arithmetically although they have the same
317     bitwise representation and compare equal by language rules.) If the numbers
318     are not arithmetically equal, `hook.onBadCast!U(get)` is
319     returned.
320 
321     */
322     U opCast(U, this _)()
323     if (isIntegral!U || isFloatingPoint!U || is(U == bool))
324     {
325         static if (hasMember!(Hook, "hookOpCast"))
326         {
327             return hook.hookOpCast!U(payload);
328         }
329         else static if (is(U == bool))
330         {
331             return payload != 0;
332         }
333         else static if (valueConvertible!(T, U))
334         {
335             return payload;
336         }
337         // may lose bits or precision
338         else static if (!hasMember!(Hook, "onBadCast"))
339         {
340             return cast(U) payload;
341         }
342         else
343         {
344             if (isUnsigned!T || !isUnsigned!U ||
345                     T.sizeof > U.sizeof || payload >= 0)
346             {
347                 auto result = cast(U) payload;
348                 // If signedness is different, we need additional checks
349                 if (result == payload &&
350                         (!isUnsigned!T || isUnsigned!U || result >= 0))
351                     return result;
352             }
353             return hook.onBadCast!U(payload);
354         }
355     }
356     ///
357     unittest
358     {
359         assert(cast(uint) checked(42) == 42);
360         assert(cast(uint) checked!WithNaN(-42) == uint.max);
361     }
362 
363     // opEquals
364     /**
365     Compares `this` against `rhs` for equality. If `Hook` defines
366     `hookOpEquals`, the function forwards to $(D
367     hook.hookOpEquals(get, rhs)). Otherwise, the result of the
368     built-in operation $(D get == rhs) is returned.
369 
370     If `U` is also an instance of `Checked`, both hooks (left- and right-hand
371     side) are introspected for the method `hookOpEquals`. If both define it,
372     priority is given to the left-hand side.
373 
374     */
375     bool opEquals(U, this _)(U rhs)
376     if (isIntegral!U || isFloatingPoint!U || is(U == bool) ||
377         is(U == Checked!(V, W), V, W) && is(typeof(this == rhs.payload)))
378     {
379         static if (is(U == Checked!(V, W), V, W))
380         {
381             alias R = typeof(payload + rhs.payload);
382             static if (is(Hook == W))
383             {
384                 // Use the lhs hook if there
385                 return this == rhs.payload;
386             }
387             else static if (valueConvertible!(T, R) && valueConvertible!(V, R))
388             {
389                 return payload == rhs.payload;
390             }
391             else static if (hasMember!(Hook, "hookOpEquals"))
392             {
393                 return hook.hookOpEquals(payload, rhs.payload);
394             }
395             else static if (hasMember!(W, "hookOpEquals"))
396             {
397                 return rhs.hook.hookOpEquals(rhs.payload, payload);
398             }
399             else
400             {
401                 return payload == rhs.payload;
402             }
403         }
404         else static if (hasMember!(Hook, "hookOpEquals"))
405             return hook.hookOpEquals(payload, rhs);
406         else static if (isIntegral!U || isFloatingPoint!U || is(U == bool))
407             return payload == rhs;
408     }
409 
410     ///
411     static if (is(T == int) && is(Hook == void)) unittest
412     {
413         static struct MyHook
414         {
415             static bool thereWereErrors;
416             static bool hookOpEquals(L, R)(L lhs, R rhs)
417             {
418                 if (lhs != rhs) return false;
419                 static if (isUnsigned!L && !isUnsigned!R)
420                 {
421                     if (lhs > 0 && rhs < 0) thereWereErrors = true;
422                 }
423                 else static if (isUnsigned!R && !isUnsigned!L)
424                     if (lhs < 0 && rhs > 0) thereWereErrors = true;
425                 // Preserve built-in behavior.
426                 return true;
427             }
428         }
429         auto a = checked!MyHook(-42);
430         assert(a == uint(-42));
431         assert(MyHook.thereWereErrors);
432         MyHook.thereWereErrors = false;
433         assert(checked!MyHook(uint(-42)) == -42);
434         assert(MyHook.thereWereErrors);
435         static struct MyHook2
436         {
437             static bool hookOpEquals(L, R)(L lhs, R rhs)
438             {
439                 return lhs == rhs;
440             }
441         }
442         MyHook.thereWereErrors = false;
443         assert(checked!MyHook2(uint(-42)) == a);
444         // Hook on left hand side takes precedence, so no errors
445         assert(!MyHook.thereWereErrors);
446     }
447 
448     // opCmp
449     /**
450 
451     Compares `this` against `rhs` for ordering. If `Hook` defines `hookOpCmp`,
452     the function forwards to $(D hook.hookOpCmp(get, rhs)). Otherwise, the
453     result of the built-in comparison operation is returned.
454 
455     If `U` is also an instance of `Checked`, both hooks (left- and right-hand
456     side) are introspected for the method `hookOpCmp`. If both define it,
457     priority is given to the left-hand side.
458 
459     */
460     auto opCmp(U, this _)(const U rhs) //const pure @safe nothrow @nogc
461     if (isIntegral!U || isFloatingPoint!U || is(U == bool))
462     {
463         static if (hasMember!(Hook, "hookOpCmp"))
464         {
465             return hook.hookOpCmp(payload, rhs);
466         }
467         else static if (valueConvertible!(T, U) || valueConvertible!(U, T))
468         {
469             return payload < rhs ? -1 : payload > rhs;
470         }
471         else static if (isFloatingPoint!U)
472         {
473             U lhs = payload;
474             return lhs < rhs ? U(-1.0)
475                 : lhs > rhs ? U(1.0)
476                 : lhs == rhs ? U(0.0) : U.init;
477         }
478         else
479         {
480             return payload < rhs ? -1 : payload > rhs;
481         }
482     }
483 
484     /// ditto
485     auto opCmp(U, Hook1, this _)(Checked!(U, Hook1) rhs)
486     {
487         alias R = typeof(payload + rhs.payload);
488         static if (valueConvertible!(T, R) && valueConvertible!(U, R))
489         {
490             return payload < rhs.payload ? -1 : payload > rhs.payload;
491         }
492         else static if (is(Hook == Hook1))
493         {
494             // Use the lhs hook
495             return this.opCmp(rhs.payload);
496         }
497         else static if (hasMember!(Hook, "hookOpCmp"))
498         {
499             return hook.hookOpCmp(get, rhs.get);
500         }
501         else static if (hasMember!(Hook1, "hookOpCmp"))
502         {
503             return -rhs.hook.hookOpCmp(rhs.payload, get);
504         }
505         else
506         {
507             return payload < rhs.payload ? -1 : payload > rhs.payload;
508         }
509     }
510 
511     ///
512     static if (is(T == int) && is(Hook == void)) unittest
513     {
514         static struct MyHook
515         {
516             static bool thereWereErrors;
517             static int hookOpCmp(L, R)(L lhs, R rhs)
518             {
519                 static if (isUnsigned!L && !isUnsigned!R)
520                 {
521                     if (rhs < 0 && rhs >= lhs)
522                         thereWereErrors = true;
523                 }
524                 else static if (isUnsigned!R && !isUnsigned!L)
525                 {
526                     if (lhs < 0 && lhs >= rhs)
527                         thereWereErrors = true;
528                 }
529                 // Preserve built-in behavior.
530                 return lhs < rhs ? -1 : lhs > rhs;
531             }
532         }
533         auto a = checked!MyHook(-42);
534         assert(a > uint(42));
535         assert(MyHook.thereWereErrors);
536         static struct MyHook2
537         {
538             static int hookOpCmp(L, R)(L lhs, R rhs)
539             {
540                 // Default behavior
541                 return lhs < rhs ? -1 : lhs > rhs;
542             }
543         }
544         MyHook.thereWereErrors = false;
545         assert(Checked!(uint, MyHook2)(uint(-42)) <= a);
546         //assert(Checked!(uint, MyHook2)(uint(-42)) >= a);
547         // Hook on left hand side takes precedence, so no errors
548         assert(!MyHook.thereWereErrors);
549         assert(a <= Checked!(uint, MyHook2)(uint(-42)));
550         assert(MyHook.thereWereErrors);
551     }
552 
553     // For coverage
554     static if (is(T == int) && is(Hook == void)) unittest
555     {
556         assert(checked(42) <= checked!void(42));
557         assert(checked!void(42) <= checked(42u));
558         assert(checked!void(42) <= checked!(void*)(42u));
559     }
560 
561     // opUnary
562     /**
563 
564     Defines unary operators `+`, `-`, `~`, `++`, and `--`. Unary `+` is not
565     overridable and always has built-in behavior (returns `this`). For the
566     others, if `Hook` defines `hookOpUnary`, `opUnary` forwards to $(D
567     Checked!(typeof(hook.hookOpUnary!op(get)),
568     Hook)(hook.hookOpUnary!op(get))).
569 
570     If `Hook` does not define `hookOpUnary` but defines `onOverflow`, `opUnary`
571     forwards to `hook.onOverflow!op(get)` in case an overflow occurs.
572     For `++` and `--`, the payload is assigned from the result of the call to
573     `onOverflow`.
574 
575     Note that unary `-` is considered to overflow if `T` is a signed integral of
576     32 or 64 bits and is equal to the most negative value. This is because that
577     value has no positive negation.
578 
579     */
580     auto opUnary(string op, this _)()
581     if (op == "+" || op == "-" || op == "~")
582     {
583         static if (op == "+")
584             return Checked(this); // "+" is not hookable
585         else static if (hasMember!(Hook, "hookOpUnary"))
586         {
587             auto r = hook.hookOpUnary!op(payload);
588             return Checked!(typeof(r), Hook)(r);
589         }
590         else static if (op == "-" && isIntegral!T && T.sizeof >= 4 &&
591                 !isUnsigned!T && hasMember!(Hook, "onOverflow"))
592         {
593             import core.checkedint;
594             static assert(is(typeof(-payload) == typeof(payload)));
595             bool overflow;
596             auto r = negs(payload, overflow);
597             if (overflow) r = hook.onOverflow!op(payload);
598             return Checked(r);
599         }
600         else
601             return Checked(mixin(op ~ "payload"));
602     }
603 
604     /// ditto
605     ref Checked opUnary(string op)() return
606     if (op == "++" || op == "--")
607     {
608         static if (hasMember!(Hook, "hookOpUnary"))
609             hook.hookOpUnary!op(payload);
610         else static if (hasMember!(Hook, "onOverflow"))
611         {
612             static if (op == "++")
613             {
614                 if (payload == max.payload)
615                     payload = hook.onOverflow!"++"(payload);
616                 else
617                     ++payload;
618             }
619             else
620             {
621                 if (payload == min.payload)
622                     payload = hook.onOverflow!"--"(payload);
623                 else
624                     --payload;
625             }
626         }
627         else
628             mixin(op ~ "payload;");
629         return this;
630     }
631 
632     ///
633     static if (is(T == int) && is(Hook == void)) unittest
634     {
635         static struct MyHook
636         {
637             static bool thereWereErrors;
638             static L hookOpUnary(string x, L)(L lhs)
639             {
640                 if (x == "-" && lhs == -lhs) thereWereErrors = true;
641                 return -lhs;
642             }
643         }
644         auto a = checked!MyHook(long.min);
645         assert(a == -a);
646         assert(MyHook.thereWereErrors);
647         auto b = checked!void(42);
648         assert(++b == 43);
649     }
650 
651     // opBinary
652     /**
653 
654     Defines binary operators `+`, `-`, `*`, `/`, `%`, `^^`, `&`, `|`, `^`, `<<`, `>>`,
655     and `>>>`. If `Hook` defines `hookOpBinary`, `opBinary` forwards to $(D
656     Checked!(typeof(hook.hookOpBinary!op(get, rhs)),
657     Hook)(hook.hookOpBinary!op(get, rhs))).
658 
659     If `Hook` does not define `hookOpBinary` but defines `onOverflow`,
660     `opBinary` forwards to `hook.onOverflow!op(get, rhs)` in case an
661     overflow occurs.
662 
663     If two `Checked` instances are involved in a binary operation and both
664     define `hookOpBinary`, the left-hand side hook has priority. If both define
665     `onOverflow`, a compile-time error occurs.
666 
667     */
668     auto opBinary(string op, Rhs)(const Rhs rhs)
669     if (isIntegral!Rhs || isFloatingPoint!Rhs || is(Rhs == bool))
670     {
671         return opBinaryImpl!(op, Rhs, typeof(this))(rhs);
672     }
673 
674     /// ditto
675     auto opBinary(string op, Rhs)(const Rhs rhs) const
676     if (isIntegral!Rhs || isFloatingPoint!Rhs || is(Rhs == bool))
677     {
678         return opBinaryImpl!(op, Rhs, typeof(this))(rhs);
679     }
680 
681     private auto opBinaryImpl(string op, Rhs, this _)(const Rhs rhs)
682     {
683         alias R = typeof(mixin("payload" ~ op ~ "rhs"));
684         static assert(is(typeof(mixin("payload" ~ op ~ "rhs")) == R));
685         static if (isIntegral!R) alias Result = Checked!(R, Hook);
686         else alias Result = R;
687 
688         static if (hasMember!(Hook, "hookOpBinary"))
689         {
690             auto r = hook.hookOpBinary!op(payload, rhs);
691             return Checked!(typeof(r), Hook)(r);
692         }
693         else static if (is(Rhs == bool))
694         {
695             return mixin("this" ~ op ~ "ubyte(rhs)");
696         }
697         else static if (isFloatingPoint!Rhs)
698         {
699             return mixin("payload" ~ op ~ "rhs");
700         }
701         else static if (hasMember!(Hook, "onOverflow"))
702         {
703             bool overflow;
704             auto r = opChecked!op(payload, rhs, overflow);
705             if (overflow) r = hook.onOverflow!op(payload, rhs);
706             return Result(r);
707         }
708         else
709         {
710             // Default is built-in behavior
711             return Result(mixin("payload" ~ op ~ "rhs"));
712         }
713     }
714 
715     /// ditto
716     auto opBinary(string op, U, Hook1)(Checked!(U, Hook1) rhs)
717     {
718         return opBinaryImpl2!(op, U, Hook1, typeof(this))(rhs);
719     }
720 
721     /// ditto
722     auto opBinary(string op, U, Hook1)(Checked!(U, Hook1) rhs) const
723     {
724         return opBinaryImpl2!(op, U, Hook1, typeof(this))(rhs);
725     }
726 
727     private
728     auto opBinaryImpl2(string op, U, Hook1, this _)(Checked!(U, Hook1) rhs)
729     {
730         alias R = typeof(get + rhs.payload);
731         static if (valueConvertible!(T, R) && valueConvertible!(U, R) ||
732             is(Hook == Hook1))
733         {
734             // Delegate to lhs
735             return mixin("this" ~ op ~ "rhs.payload");
736         }
737         else static if (hasMember!(Hook, "hookOpBinary"))
738         {
739             return hook.hookOpBinary!op(payload, rhs);
740         }
741         else static if (hasMember!(Hook1, "hookOpBinary"))
742         {
743             // Delegate to rhs
744             return mixin("this.payload" ~ op ~ "rhs");
745         }
746         else static if (hasMember!(Hook, "onOverflow") &&
747             !hasMember!(Hook1, "onOverflow"))
748         {
749             // Delegate to lhs
750             return mixin("this" ~ op ~ "rhs.payload");
751         }
752         else static if (hasMember!(Hook1, "onOverflow") &&
753             !hasMember!(Hook, "onOverflow"))
754         {
755             // Delegate to rhs
756             return mixin("this.payload" ~ op ~ "rhs");
757         }
758         else
759         {
760             static assert(0, "Conflict between lhs and rhs hooks," ~
761                 " use .get on one side to disambiguate.");
762         }
763     }
764 
765     static if (is(T == int) && is(Hook == void)) unittest
766     {
767         const a = checked(42);
768         assert(a + 1 == 43);
769         assert(a + checked(uint(42)) == 84);
770         assert(checked(42) + checked!void(42u) == 84);
771         assert(checked!void(42) + checked(42u) == 84);
772 
773         static struct MyHook
774         {
775             static uint tally;
776             static auto hookOpBinary(string x, L, R)(L lhs, R rhs)
777             {
778                 ++tally;
779                 return mixin("lhs" ~ x ~ "rhs");
780             }
781         }
782         assert(checked!MyHook(42) + checked(42u) == 84);
783         assert(checked!void(42) + checked!MyHook(42u) == 84);
784         assert(MyHook.tally == 2);
785     }
786 
787     // opBinaryRight
788     /**
789 
790     Defines binary operators `+`, `-`, `*`, `/`, `%`, `^^`, `&`, `|`, `^`, `<<`,
791     `>>`, and `>>>` for the case when a built-in numeric or Boolean type is on
792     the left-hand side, and a `Checked` instance is on the right-hand side.
793 
794     */
795     auto opBinaryRight(string op, Lhs)(const Lhs lhs)
796     if (isIntegral!Lhs || isFloatingPoint!Lhs || is(Lhs == bool))
797     {
798         return opBinaryRightImpl!(op, Lhs, typeof(this))(lhs);
799     }
800 
801     /// ditto
802     auto opBinaryRight(string op, Lhs)(const Lhs lhs) const
803     if (isIntegral!Lhs || isFloatingPoint!Lhs || is(Lhs == bool))
804     {
805         return opBinaryRightImpl!(op, Lhs, typeof(this))(lhs);
806     }
807 
808     private auto opBinaryRightImpl(string op, Lhs, this _)(const Lhs lhs)
809     {
810         static if (hasMember!(Hook, "hookOpBinaryRight"))
811         {
812             auto r = hook.hookOpBinaryRight!op(lhs, payload);
813             return Checked!(typeof(r), Hook)(r);
814         }
815         else static if (hasMember!(Hook, "hookOpBinary"))
816         {
817             auto r = hook.hookOpBinary!op(lhs, payload);
818             return Checked!(typeof(r), Hook)(r);
819         }
820         else static if (is(Lhs == bool))
821         {
822             return mixin("ubyte(lhs)" ~ op ~ "this");
823         }
824         else static if (isFloatingPoint!Lhs)
825         {
826             return mixin("lhs" ~ op ~ "payload");
827         }
828         else static if (hasMember!(Hook, "onOverflow"))
829         {
830             bool overflow;
831             auto r = opChecked!op(lhs, T(payload), overflow);
832             if (overflow) r = hook.onOverflow!op(42);
833             return Checked!(typeof(r), Hook)(r);
834         }
835         else
836         {
837             // Default is built-in behavior
838             auto r = mixin("lhs" ~ op ~ "T(payload)");
839             return Checked!(typeof(r), Hook)(r);
840         }
841     }
842 
843     static if (is(T == int) && is(Hook == void)) unittest
844     {
845         assert(1 + checked(1) == 2);
846         static uint tally;
847         static struct MyHook
848         {
849             static auto hookOpBinaryRight(string x, L, R)(L lhs, R rhs)
850             {
851                 ++tally;
852                 return mixin("lhs" ~ x ~ "rhs");
853             }
854         }
855         assert(1 + checked!MyHook(1) == 2);
856         assert(tally == 1);
857 
858         immutable x1 = checked(1);
859         assert(1 + x1 == 2);
860         immutable x2 = checked!MyHook(1);
861         assert(1 + x2 == 2);
862         assert(tally == 2);
863     }
864 
865     // opOpAssign
866     /**
867 
868     Defines operators `+=`, `-=`, `*=`, `/=`, `%=`, `^^=`, `&=`, `|=`, `^=`,
869     `<<=`, `>>=`, and `>>>=`.
870 
871     If `Hook` defines `hookOpOpAssign`, `opOpAssign` forwards to
872     `hook.hookOpOpAssign!op(payload, rhs)`, where `payload` is a reference to
873     the internally held data so the hook can change it.
874 
875     Otherwise, the operator first evaluates $(D auto result =
876     opBinary!op(payload, rhs).payload), which is subject to the hooks in
877     `opBinary`. Then, if `result` is less than $(D Checked!(T, Hook).min) and if
878     `Hook` defines `onLowerBound`, the payload is assigned from $(D
879     hook.onLowerBound(result, min)). If `result` is greater than $(D Checked!(T,
880     Hook).max) and if `Hook` defines `onUpperBound`, the payload is assigned
881     from $(D hook.onUpperBound(result, min)).
882 
883     In all other cases, the built-in behavior is carried out.
884 
885     Params:
886     op = The operator involved (without the `"="`, e.g. `"+"` for `"+="` etc)
887     rhs = The right-hand side of the operator (left-hand side is `this`)
888 
889     Returns: A reference to `this`.
890     */
891     ref Checked opOpAssign(string op, Rhs)(const Rhs rhs) return
892     if (isIntegral!Rhs || isFloatingPoint!Rhs || is(Rhs == bool))
893     {
894         static assert(is(typeof(mixin("payload" ~ op ~ "=rhs")) == T));
895 
896         static if (hasMember!(Hook, "hookOpOpAssign"))
897         {
898             hook.hookOpOpAssign!op(payload, rhs);
899         }
900         else
901         {
902             alias R = typeof(get + rhs);
903             auto r = opBinary!op(rhs).get;
904             import std.conv : unsigned;
905 
906             static if (ProperCompare.hookOpCmp(R.min, min.get) < 0 &&
907                 hasMember!(Hook, "onLowerBound"))
908             {
909                 if (ProperCompare.hookOpCmp(r, min.get) < 0)
910                 {
911                     // Example: Checked!uint(1) += int(-3)
912                     payload = hook.onLowerBound(r, min.get);
913                     return this;
914                 }
915             }
916             static if (ProperCompare.hookOpCmp(max.get, R.max) < 0 &&
917                 hasMember!(Hook, "onUpperBound"))
918             {
919                 if (ProperCompare.hookOpCmp(r, max.get) > 0)
920                 {
921                     // Example: Checked!uint(1) += long(uint.max)
922                     payload = hook.onUpperBound(r, max.get);
923                     return this;
924                 }
925             }
926             payload = cast(T) r;
927         }
928         return this;
929     }
930 
931     ///
932     static if (is(T == int) && is(Hook == void)) unittest
933     {
934         static struct MyHook
935         {
936             static bool thereWereErrors;
937             static T onLowerBound(Rhs, T)(Rhs rhs, T bound)
938             {
939                 thereWereErrors = true;
940                 return bound;
941             }
942             static T onUpperBound(Rhs, T)(Rhs rhs, T bound)
943             {
944                 thereWereErrors = true;
945                 return bound;
946             }
947         }
948         auto x = checked!MyHook(byte.min);
949         x -= 1;
950         assert(MyHook.thereWereErrors);
951         MyHook.thereWereErrors = false;
952         x = byte.max;
953         x += 1;
954         assert(MyHook.thereWereErrors);
955     }
956 }
957 
958 /**
959 
960 Convenience function that turns an integral into the corresponding `Checked`
961 instance by using template argument deduction. The hook type may be specified
962 (by default `Abort`).
963 
964 */
965 Checked!(T, Hook) checked(Hook = Abort, T)(const T value)
966 if (is(typeof(Checked!(T, Hook)(value))))
967 {
968     return Checked!(T, Hook)(value);
969 }
970 
971 ///
972 unittest
973 {
974     static assert(is(typeof(checked(42)) == Checked!int));
975     assert(checked(42) == Checked!int(42));
976     static assert(is(typeof(checked!WithNaN(42)) == Checked!(int, WithNaN)));
977     assert(checked!WithNaN(42) == Checked!(int, WithNaN)(42));
978 }
979 
980 // get
981 unittest
982 {
983     void test(T)()
984     {
985         assert(Checked!(T, void)(ubyte(22)).get == 22);
986     }
987     test!ubyte;
988     test!(const ubyte);
989     test!(immutable ubyte);
990 }
991 
992 // Abort
993 /**
994 
995 Force all integral errors to fail by printing an error message to `stderr` and
996 then abort the program. `Abort` is the default second argument for `Checked`.
997 
998 */
999 struct Abort
1000 {
1001 static:
1002     /**
1003 
1004     Called automatically upon a bad cast (one that loses precision or attempts
1005     to convert a negative value to an unsigned type). The source type is `Src`
1006     and the destination type is `Dst`.
1007 
1008     Params:
1009     src = The source of the cast
1010 
1011     Returns: Nominally the result is the desired value of the cast operation,
1012     which will be forwarded as the result of the cast. For `Abort`, the
1013     function never returns because it aborts the program.
1014 
1015     */
1016     Dst onBadCast(Dst, Src)(Src src)
1017     {
1018         Warn.onBadCast!Dst(src);
1019         assert(0);
1020     }
1021 
1022     /**
1023 
1024     Called automatically upon a bounds error.
1025 
1026     Params:
1027     rhs = The right-hand side value in the assignment, after the operator has
1028     been evaluated
1029     bound = The value of the bound being violated
1030 
1031     Returns: Nominally the result is the desired value of the operator, which
1032     will be forwarded as result. For `Abort`, the function never returns because
1033     it aborts the program.
1034 
1035     */
1036     T onLowerBound(Rhs, T)(Rhs rhs, T bound)
1037     {
1038         Warn.onLowerBound(rhs, bound);
1039         assert(0);
1040     }
1041     /// ditto
1042     T onUpperBound(Rhs, T)(Rhs rhs, T bound)
1043     {
1044         Warn.onUpperBound(rhs, bound);
1045         assert(0);
1046     }
1047 
1048     /**
1049 
1050     Called automatically upon a comparison for equality. In case of a erroneous
1051     comparison (one that would make a signed negative value appear equal to an
1052     unsigned positive value), this hook issues `assert(0)` which terminates the
1053     application.
1054 
1055     Params:
1056     lhs = The first argument of `Checked`, e.g. `int` if the left-hand side of
1057       the operator is `Checked!int`
1058     rhs = The right-hand side type involved in the operator
1059 
1060     Returns: Upon a correct comparison, returns the result of the comparison.
1061     Otherwise, the function terminates the application so it never returns.
1062 
1063     */
1064     static bool hookOpEquals(Lhs, Rhs)(Lhs lhs, Rhs rhs)
1065     {
1066         bool error;
1067         auto result = opChecked!"=="(lhs, rhs, error);
1068         if (error)
1069         {
1070             Warn.hookOpEquals(lhs, rhs);
1071             assert(0);
1072         }
1073         return result;
1074     }
1075 
1076     /**
1077 
1078     Called automatically upon a comparison for ordering using one of the
1079     operators `<`, `<=`, `>`, or `>=`. In case the comparison is erroneous (i.e.
1080     it would make a signed negative value appear greater than or equal to an
1081     unsigned positive value), then application is terminated with `assert(0)`.
1082     Otherwise, the three-state result is returned (positive if $(D lhs > rhs),
1083     negative if $(D lhs < rhs), `0` otherwise).
1084 
1085     Params:
1086     lhs = The first argument of `Checked`, e.g. `int` if the left-hand side of
1087       the operator is `Checked!int`
1088     rhs = The right-hand side type involved in the operator
1089 
1090     Returns: For correct comparisons, returns a positive integer if $(D lhs >
1091     rhs), a negative integer if  $(D lhs < rhs), `0` if the two are equal. Upon
1092     a mistaken comparison such as $(D int(-1) < uint(0)), the function never
1093     returns because it aborts the program.
1094 
1095     */
1096     int hookOpCmp(Lhs, Rhs)(Lhs lhs, Rhs rhs)
1097     {
1098         bool error;
1099         auto result = opChecked!"cmp"(lhs, rhs, error);
1100         if (error)
1101         {
1102             Warn.hookOpCmp(lhs, rhs);
1103             assert(0);
1104         }
1105         return result;
1106     }
1107 
1108     /**
1109 
1110     Called automatically upon an overflow during a unary or binary operation.
1111 
1112     Params:
1113     x = The operator, e.g. `-`
1114     lhs = The left-hand side (or sole) argument
1115     rhs = The right-hand side type involved in the operator
1116 
1117     Returns: Nominally the result is the desired value of the operator, which
1118     will be forwarded as result. For `Abort`, the function never returns because
1119     it aborts the program.
1120 
1121     */
1122     typeof(~Lhs()) onOverflow(string x, Lhs)(Lhs lhs)
1123     {
1124         Warn.onOverflow!x(lhs);
1125         assert(0);
1126     }
1127     /// ditto
1128     typeof(Lhs() + Rhs()) onOverflow(string x, Lhs, Rhs)(Lhs lhs, Rhs rhs)
1129     {
1130         Warn.onOverflow!x(lhs, rhs);
1131         assert(0);
1132     }
1133 }
1134 
1135 unittest
1136 {
1137     void test(T)()
1138     {
1139         Checked!(int, Abort) x;
1140         x = 42;
1141         auto x1 = cast(T) x;
1142         assert(x1 == 42);
1143         //x1 += long(int.max);
1144     }
1145     test!short;
1146     test!(const short);
1147     test!(immutable short);
1148 }
1149 
1150 
1151 // Throw
1152 /**
1153 
1154 Force all integral errors to fail by throwing an exception of type
1155 `Throw.CheckFailure`. The message coming with the error is similar to the one
1156 printed by `Warn`.
1157 
1158 */
1159 struct Throw
1160 {
1161     /**
1162     Exception type thrown upon any failure.
1163     */
1164     static class CheckFailure : Exception
1165     {
1166         this(T...)(string f, T vals)
1167         {
1168             import std.format;
1169             super(format(f, vals));
1170         }
1171     }
1172 
1173     /**
1174 
1175     Called automatically upon a bad cast (one that loses precision or attempts
1176     to convert a negative value to an unsigned type). The source type is `Src`
1177     and the destination type is `Dst`.
1178 
1179     Params:
1180     src = The source of the cast
1181 
1182     Returns: Nominally the result is the desired value of the cast operation,
1183     which will be forwarded as the result of the cast. For `Throw`, the
1184     function never returns because it throws an exception.
1185 
1186     */
1187     static Dst onBadCast(Dst, Src)(Src src)
1188     {
1189         throw new CheckFailure("Erroneous cast: cast(%s) %s(%s)",
1190             Dst.stringof, Src.stringof, src);
1191     }
1192 
1193     /**
1194 
1195     Called automatically upon a bounds error.
1196 
1197     Params:
1198     rhs = The right-hand side value in the assignment, after the operator has
1199     been evaluated
1200     bound = The value of the bound being violated
1201 
1202     Returns: Nominally the result is the desired value of the operator, which
1203     will be forwarded as result. For `Throw`, the function never returns because
1204     it throws.
1205 
1206     */
1207     static T onLowerBound(Rhs, T)(Rhs rhs, T bound)
1208     {
1209         throw new CheckFailure("Lower bound error: %s(%s) < %s(%s)",
1210             Rhs.stringof, rhs, T.stringof, bound);
1211     }
1212     /// ditto
1213     static T onUpperBound(Rhs, T)(Rhs rhs, T bound)
1214     {
1215         throw new CheckFailure("Upper bound error: %s(%s) > %s(%s)",
1216             Rhs.stringof, rhs, T.stringof, bound);
1217     }
1218 
1219     /**
1220 
1221     Called automatically upon a comparison for equality. Throws upon an
1222     erroneous comparison (one that would make a signed negative value appear
1223     equal to an unsigned positive value).
1224 
1225     Params:
1226     lhs = The first argument of `Checked`, e.g. `int` if the left-hand side of
1227       the operator is `Checked!int`
1228     rhs = The right-hand side type involved in the operator
1229 
1230     Returns: The result of the comparison.
1231 
1232     Throws: `CheckFailure` if the comparison is mathematically erroneous.
1233 
1234     */
1235     static bool hookOpEquals(L, R)(L lhs, R rhs)
1236     {
1237         bool error;
1238         auto result = opChecked!"=="(lhs, rhs, error);
1239         if (error)
1240         {
1241             throw new CheckFailure("Erroneous comparison: %s(%s) == %s(%s)",
1242                 L.stringof, lhs, R.stringof, rhs);
1243         }
1244         return result;
1245     }
1246 
1247     /**
1248 
1249     Called automatically upon a comparison for ordering using one of the
1250     operators `<`, `<=`, `>`, or `>=`. In case the comparison is erroneous (i.e.
1251     it would make a signed negative value appear greater than or equal to an
1252     unsigned positive value), throws a `Throw.CheckFailure` exception.
1253     Otherwise, the three-state result is returned (positive if $(D lhs > rhs),
1254     negative if $(D lhs < rhs), `0` otherwise).
1255 
1256     Params:
1257     lhs = The first argument of `Checked`, e.g. `int` if the left-hand side of
1258       the operator is `Checked!int`
1259     rhs = The right-hand side type involved in the operator
1260 
1261     Returns: For correct comparisons, returns a positive integer if $(D lhs >
1262     rhs), a negative integer if  $(D lhs < rhs), `0` if the two are equal.
1263 
1264     Throws: Upon a mistaken comparison such as $(D int(-1) < uint(0)), the
1265     function never returns because it throws a `Throw.CheckedFailure` exception.
1266 
1267     */
1268     static int hookOpCmp(Lhs, Rhs)(Lhs lhs, Rhs rhs)
1269     {
1270         bool error;
1271         auto result = opChecked!"cmp"(lhs, rhs, error);
1272         if (error)
1273         {
1274             throw new CheckFailure("Erroneous ordering comparison: %s(%s) and %s(%s)",
1275                 Lhs.stringof, lhs, Rhs.stringof, rhs);
1276         }
1277         return result;
1278     }
1279 
1280     /**
1281 
1282     Called automatically upon an overflow during a unary or binary operation.
1283 
1284     Params:
1285     x = The operator, e.g. `-`
1286     lhs = The left-hand side (or sole) argument
1287     rhs = The right-hand side type involved in the operator
1288 
1289     Returns: Nominally the result is the desired value of the operator, which
1290     will be forwarded as result. For `Throw`, the function never returns because
1291     it throws an exception.
1292 
1293     */
1294     static typeof(~Lhs()) onOverflow(string x, Lhs)(Lhs lhs)
1295     {
1296         throw new CheckFailure("Overflow on unary operator: %s%s(%s)",
1297             x, Lhs.stringof, lhs);
1298     }
1299     /// ditto
1300     static typeof(Lhs() + Rhs()) onOverflow(string x, Lhs, Rhs)(Lhs lhs, Rhs rhs)
1301     {
1302         throw new CheckFailure("Overflow on binary operator: %s(%s) %s %s(%s)",
1303             Lhs.stringof, lhs, x, Rhs.stringof, rhs);
1304     }
1305 }
1306 
1307 ///
1308 unittest
1309 {
1310     void test(T)()
1311     {
1312         Checked!(int, Throw) x;
1313         x = 42;
1314         auto x1 = cast(T) x;
1315         assert(x1 == 42);
1316         x = T.max + 1;
1317         import std.exception;
1318         assertThrown(cast(T) x);
1319         x = x.max;
1320         assertThrown(x += 42);
1321         assertThrown(x += 42L);
1322         x = x.min;
1323         assertThrown(-x);
1324         assertThrown(x -= 42);
1325         assertThrown(x -= 42L);
1326         x = -1;
1327         assertNotThrown(x == -1);
1328         assertThrown(x == uint(-1));
1329         assertNotThrown(x <= -1);
1330         assertThrown(x <= uint(-1));
1331     }
1332     test!short;
1333     test!(const short);
1334     test!(immutable short);
1335 }
1336 
1337 // Warn
1338 /**
1339 Hook that prints to `stderr` a trace of all integral errors, without affecting
1340 default behavior.
1341 */
1342 struct Warn
1343 {
1344     import std.stdio : stderr;
1345 static:
1346     /**
1347 
1348     Called automatically upon a bad cast from `src` to type `Dst` (one that
1349     loses precision or attempts to convert a negative value to an unsigned
1350     type).
1351 
1352     Params:
1353     src = The source of the cast
1354     Dst = The target type of the cast
1355 
1356     Returns: `cast(Dst) src`
1357 
1358     */
1359     Dst onBadCast(Dst, Src)(Src src)
1360     {
1361         stderr.writefln("Erroneous cast: cast(%s) %s(%s)",
1362             Dst.stringof, Src.stringof, src);
1363         return cast(Dst) src;
1364     }
1365 
1366     /**
1367 
1368     Called automatically upon a bad `opOpAssign` call (one that loses precision
1369     or attempts to convert a negative value to an unsigned type).
1370 
1371     Params:
1372     rhs = The right-hand side value in the assignment, after the operator has
1373     been evaluated
1374     bound = The bound being violated
1375 
1376     Returns: `cast(Lhs) rhs`
1377     */
1378     Lhs onLowerBound(Rhs, T)(Rhs rhs, T bound)
1379     {
1380         stderr.writefln("Lower bound error: %s(%s) < %s(%s)",
1381             Rhs.stringof, rhs, T.stringof, bound);
1382         return cast(T) rhs;
1383     }
1384     /// ditto
1385     T onUpperBound(Rhs, T)(Rhs rhs, T bound)
1386     {
1387         stderr.writefln("Upper bound error: %s(%s) > %s(%s)",
1388             Rhs.stringof, rhs, T.stringof, bound);
1389         return cast(T) rhs;
1390     }
1391 
1392     /**
1393 
1394     Called automatically upon a comparison for equality. In case of an Erroneous
1395     comparison (one that would make a signed negative value appear equal to an
1396     unsigned positive value), writes a warning message to `stderr` as a side
1397     effect.
1398 
1399     Params:
1400     lhs = The first argument of `Checked`, e.g. `int` if the left-hand side of
1401       the operator is `Checked!int`
1402     rhs = The right-hand side type involved in the operator
1403 
1404     Returns: In all cases the function returns the built-in result of $(D lhs ==
1405     rhs).
1406 
1407     */
1408     bool hookOpEquals(Lhs, Rhs)(Lhs lhs, Rhs rhs)
1409     {
1410         bool error;
1411         auto result = opChecked!"=="(lhs, rhs, error);
1412         if (error)
1413         {
1414             stderr.writefln("Erroneous comparison: %s(%s) == %s(%s)",
1415                 Lhs.stringof, lhs, Rhs.stringof, rhs);
1416             return lhs == rhs;
1417         }
1418         return result;
1419     }
1420 
1421     ///
1422     unittest
1423     {
1424         auto x = checked!Warn(-42);
1425         // Passes
1426         assert(x == -42);
1427         // Passes but prints a warning
1428         // assert(x == uint(-42));
1429     }
1430 
1431     /**
1432 
1433     Called automatically upon a comparison for ordering using one of the
1434     operators `<`, `<=`, `>`, or `>=`. In case the comparison is erroneous (i.e.
1435     it would make a signed negative value appear greater than or equal to an
1436     unsigned positive value), then a warning message is printed to `stderr`.
1437 
1438     Params:
1439     lhs = The first argument of `Checked`, e.g. `int` if the left-hand side of
1440       the operator is `Checked!int`
1441     rhs = The right-hand side type involved in the operator
1442 
1443     Returns: In all cases, returns $(D lhs < rhs ? -1 : lhs > rhs). The result
1444     is  not autocorrected in case of an erroneous comparison.
1445 
1446     */
1447     int hookOpCmp(Lhs, Rhs)(Lhs lhs, Rhs rhs)
1448     {
1449         bool error;
1450         auto result = opChecked!"cmp"(lhs, rhs, error);
1451         if (error)
1452         {
1453             stderr.writefln("Erroneous ordering comparison: %s(%s) and %s(%s)",
1454                 Lhs.stringof, lhs, Rhs.stringof, rhs);
1455             return lhs < rhs ? -1 : lhs > rhs;
1456         }
1457         return result;
1458     }
1459 
1460     ///
1461     unittest
1462     {
1463         auto x = checked!Warn(-42);
1464         // Passes
1465         assert(x <= -42);
1466         // Passes but prints a warning
1467         // assert(x <= uint(-42));
1468     }
1469 
1470     /**
1471 
1472     Called automatically upon an overflow during a unary or binary operation.
1473 
1474     Params:
1475     x = The operator involved
1476     Lhs = The first argument of `Checked`, e.g. `int` if the left-hand side of
1477       the operator is `Checked!int`
1478     Rhs = The right-hand side type involved in the operator
1479 
1480     Returns: $(D mixin(x ~ "lhs")) for unary, $(D mixin("lhs" ~ x ~ "rhs")) for
1481     binary
1482 
1483     */
1484     typeof(~Lhs()) onOverflow(string x, Lhs)(ref Lhs lhs)
1485     {
1486         stderr.writefln("Overflow on unary operator: %s%s(%s)",
1487             x, Lhs.stringof, lhs);
1488         return mixin(x ~ "lhs");
1489     }
1490     /// ditto
1491     typeof(Lhs() + Rhs()) onOverflow(string x, Lhs, Rhs)(Lhs lhs, Rhs rhs)
1492     {
1493         stderr.writefln("Overflow on binary operator: %s(%s) %s %s(%s)",
1494             Lhs.stringof, lhs, x, Rhs.stringof, rhs);
1495         return mixin("lhs" ~ x ~ "rhs");
1496     }
1497 }
1498 
1499 ///
1500 unittest
1501 {
1502     auto x = checked!Warn(42);
1503     short x1 = cast(short) x;
1504     //x += long(int.max);
1505     auto y = checked!Warn(cast(const int) 42);
1506     short y1 = cast(const byte) y;
1507 }
1508 
1509 // ProperCompare
1510 /**
1511 
1512 Hook that provides arithmetically correct comparisons for equality and ordering.
1513 Comparing an object of type $(D Checked!(X, ProperCompare)) against another
1514 integral (for equality or ordering) ensures that no surprising conversions from
1515 signed to unsigned integral occur before the comparison. Using $(D Checked!(X,
1516 ProperCompare)) on either side of a comparison for equality against a
1517 floating-point number makes sure the integral can be properly converted to the
1518 floating point type, thus making sure equality is transitive.
1519 
1520 */
1521 struct ProperCompare
1522 {
1523     /**
1524     Hook for `==` and `!=` that ensures comparison against integral values has
1525     the behavior expected by the usual arithmetic rules. The built-in semantics
1526     yield surprising behavior when comparing signed values against unsigned
1527     values for equality, for example $(D uint.max == -1) or $(D -1_294_967_296 ==
1528     3_000_000_000u). The call $(D hookOpEquals(x, y)) returns `true` if and only
1529     if `x` and `y` represent the same arithmetic number.
1530 
1531     If one of the numbers is an integral and the other is a floating-point
1532     number, $(D hookOpEquals(x, y)) returns `true` if and only if the integral
1533     can be converted exactly (without approximation) to the floating-point
1534     number. This is in order to preserve transitivity of equality: if $(D
1535     hookOpEquals(x, y)) and $(D hookOpEquals(y, z)) then $(D hookOpEquals(y,
1536     z)), in case `x`, `y`, and `z` are a mix of integral and floating-point
1537     numbers.
1538 
1539     Params:
1540     lhs = The left-hand side of the comparison for equality
1541     rhs = The right-hand side of the comparison for equality
1542 
1543     Returns:
1544     The result of the comparison, `true` if the values are equal
1545     */
1546     static bool hookOpEquals(L, R)(L lhs, R rhs)
1547     {
1548         alias C = typeof(lhs + rhs);
1549         static if (isFloatingPoint!C)
1550         {
1551             static if (!isFloatingPoint!L)
1552             {
1553                 return hookOpEquals(rhs, lhs);
1554             }
1555             else static if (!isFloatingPoint!R)
1556             {
1557                 static assert(isFloatingPoint!L && !isFloatingPoint!R);
1558                 auto rhs1 = C(rhs);
1559                 return lhs == rhs1 && cast(R) rhs1 == rhs;
1560             }
1561             else
1562                 return lhs == rhs;
1563         }
1564         else
1565         {
1566             bool error;
1567             auto result = opChecked!"=="(lhs, rhs, error);
1568             if (error)
1569             {
1570                 // Only possible error is a wrong "true"
1571                 return false;
1572             }
1573             return result;
1574         }
1575     }
1576 
1577     /**
1578     Hook for `<`, `<=`, `>`, and `>=` that ensures comparison against integral
1579     values has the behavior expected by the usual arithmetic rules. The built-in
1580     semantics yield surprising behavior when comparing signed values against
1581     unsigned values, for example $(D 0u < -1). The call $(D hookOpCmp(x, y))
1582     returns `-1` if and only if `x` is smaller than `y` in abstract arithmetic
1583     sense.
1584 
1585     If one of the numbers is an integral and the other is a floating-point
1586     number, $(D hookOpEquals(x, y)) returns a floating-point number that is `-1`
1587     if `x < y`, `0` if `x == y`, `1` if `x > y`, and `NaN` if the floating-point
1588     number is `NaN`.
1589 
1590     Params:
1591     lhs = The left-hand side of the comparison for ordering
1592     rhs = The right-hand side of the comparison for ordering
1593 
1594     Returns:
1595     The result of the comparison (negative if $(D lhs < rhs), positive if $(D
1596     lhs > rhs), `0` if the values are equal)
1597     */
1598     static auto hookOpCmp(L, R)(L lhs, R rhs)
1599     {
1600         alias C = typeof(lhs + rhs);
1601         static if (isFloatingPoint!C)
1602         {
1603             return lhs < rhs
1604                 ? C(-1)
1605                 : lhs > rhs ? C(1) : lhs == rhs ? C(0) : C.init;
1606         }
1607         else
1608         {
1609             static if (!valueConvertible!(L, C) || !valueConvertible!(R, C))
1610             {
1611                 static assert(isUnsigned!C);
1612                 static assert(isUnsigned!L != isUnsigned!R);
1613                 if (!isUnsigned!L && lhs < 0)
1614                     return -1;
1615                 if (!isUnsigned!R && rhs < 0)
1616                     return 1;
1617             }
1618             return lhs < rhs ? -1 : lhs > rhs;
1619         }
1620     }
1621 }
1622 
1623 ///
1624 unittest
1625 {
1626     alias opEqualsProper = ProperCompare.hookOpEquals;
1627     assert(opEqualsProper(42, 42));
1628     assert(opEqualsProper(42.0, 42.0));
1629     assert(opEqualsProper(42u, 42));
1630     assert(opEqualsProper(42, 42u));
1631     assert(-1 == 4294967295u);
1632     assert(!opEqualsProper(-1, 4294967295u));
1633     assert(!opEqualsProper(const uint(-1), -1));
1634     assert(!opEqualsProper(uint(-1), -1.0));
1635     assert(3_000_000_000U == -1_294_967_296);
1636     assert(!opEqualsProper(3_000_000_000U, -1_294_967_296));
1637 }
1638 
1639 unittest
1640 {
1641     alias opCmpProper = ProperCompare.hookOpCmp;
1642     assert(opCmpProper(42, 42) == 0);
1643     assert(opCmpProper(42, 42.0) == 0);
1644     assert(opCmpProper(41, 42.0) < 0);
1645     assert(opCmpProper(42, 41.0) > 0);
1646     assert(opCmpProper(41, double.init) is double.init);
1647     assert(opCmpProper(42u, 42) == 0);
1648     assert(opCmpProper(42, 42u) == 0);
1649     assert(opCmpProper(-1, uint(-1)) < 0);
1650     assert(opCmpProper(uint(-1), -1) > 0);
1651     assert(opCmpProper(-1.0, -1) == 0);
1652 }
1653 
1654 unittest
1655 {
1656     auto x1 = Checked!(uint, ProperCompare)(42u);
1657     assert(x1.get < -1);
1658     assert(x1 > -1);
1659 }
1660 
1661 // WithNaN
1662 /**
1663 
1664 Hook that reserves a special value as a "Not a Number" representative. For
1665 signed integrals, the reserved value is `T.min`. For signed integrals, the
1666 reserved value is `T.max`.
1667 
1668 The default value of a $(D Checked!(X, WithNaN)) is its NaN value, so care must
1669 be taken that all variables are explicitly initialized. Any arithmetic and logic
1670 operation involving at least on NaN becomes NaN itself. All of $(D a == b), $(D
1671 a < b), $(D a > b), $(D a <= b), $(D a >= b) yield `false` if at least one of
1672 `a` and `b` is NaN.
1673 
1674 */
1675 struct WithNaN
1676 {
1677 static:
1678     /**
1679     The default value used for values not explicitly initialized. It is the NaN
1680     value, i.e. `T.min` for signed integrals and `T.max` for unsigned integrals.
1681     */
1682     enum T defaultValue(T) = T.min == 0 ? T.max : T.min;
1683     /**
1684     The maximum value representable is $(D T.max) for signed integrals, $(D
1685     T.max - 1) for unsigned integrals. The minimum value representable is $(D
1686     T.min + 1) for signed integrals, $(D 0) for unsigned integrals.
1687     */
1688     enum T max(T) = cast(T) (T.min == 0 ? T.max - 1 : T.max);
1689     /// ditto
1690     enum T min(T) = cast(T) (T.min == 0 ? T(0) : T.min + 1);
1691 
1692     /**
1693     If `rhs` is `WithNaN.defaultValue!Rhs`, returns
1694     `WithNaN.defaultValue!Lhs`. Otherwise, returns $(D cast(Lhs) rhs).
1695 
1696     Params:
1697     rhs = the value being cast (`Rhs` is the first argument to `Checked`)
1698     Lhs = the target type of the cast
1699 
1700     Returns: The result of the cast operation.
1701     */
1702     Lhs hookOpCast(Lhs, Rhs)(Rhs rhs)
1703     {
1704         static if (is(Lhs == bool))
1705         {
1706             return rhs != defaultValue!Rhs && rhs != 0;
1707         }
1708         else static if (valueConvertible!(Rhs, Lhs))
1709         {
1710             return rhs != defaultValue!Rhs ? Lhs(rhs) : defaultValue!Lhs;
1711         }
1712         else
1713         {
1714             // Not value convertible, only viable option is rhs fits within the
1715             // bounds of Lhs
1716             static if (ProperCompare.hookOpCmp(Rhs.min, Lhs.min) < 0)
1717             {
1718                 // Example: hookOpCast!short(int(42)), hookOpCast!uint(int(42))
1719                 if (ProperCompare.hookOpCmp(rhs, Lhs.min) < 0)
1720                     return defaultValue!Lhs;
1721             }
1722             static if (ProperCompare.hookOpCmp(Rhs.max, Lhs.max) > 0)
1723             {
1724                 // Example: hookOpCast!int(uint(42))
1725                 if (ProperCompare.hookOpCmp(rhs, Lhs.max) > 0)
1726                     return defaultValue!Lhs;
1727             }
1728             return cast(Lhs) rhs;
1729         }
1730     }
1731 
1732     ///
1733     unittest
1734     {
1735         auto x = checked!WithNaN(422);
1736         assert((cast(ubyte) x) == 255);
1737         x = checked!WithNaN(-422);
1738         assert((cast(byte) x) == -128);
1739         assert(cast(short) x == -422);
1740         assert(cast(bool) x);
1741         x = x.init; // set back to NaN
1742         assert(x != true);
1743         assert(x != false);
1744     }
1745 
1746     /**
1747 
1748     Returns `false` if $(D lhs == WithNaN.defaultValue!Lhs), $(D lhs == rhs)
1749     otherwise.
1750 
1751     Params:
1752     lhs = The left-hand side of the comparison (`Lhs` is the first argument to
1753     `Checked`)
1754     rhs = The right-hand side of the comparison
1755 
1756     Returns: `lhs != WithNaN.defaultValue!Lhs && lhs == rhs`
1757     */
1758     bool hookOpEquals(Lhs, Rhs)(Lhs lhs, Rhs rhs)
1759     {
1760         return lhs != defaultValue!Lhs && lhs == rhs;
1761     }
1762 
1763     /**
1764 
1765     If $(D lhs == WithNaN.defaultValue!Lhs), returns `double.init`. Otherwise,
1766     has the same semantics as the default comparison.
1767 
1768     Params:
1769     lhs = The left-hand side of the comparison (`Lhs` is the first argument to
1770     `Checked`)
1771     rhs = The right-hand side of the comparison
1772 
1773     Returns: `double.init` if `lhs == WitnNaN.defaultValue!Lhs`, `-1.0` if $(D
1774     lhs < rhs), `0.0` if $(D lhs == rhs), `1.0` if $(D lhs > rhs).
1775 
1776     */
1777     double hookOpCmp(Lhs, Rhs)(Lhs lhs, Rhs rhs)
1778     {
1779         if (lhs == defaultValue!Lhs) return double.init;
1780         return lhs < rhs
1781             ? -1.0
1782             : lhs > rhs ? 1.0 : lhs == rhs ? 0.0 : double.init;
1783     }
1784 
1785     ///
1786     unittest
1787     {
1788         Checked!(int, WithNaN) x;
1789         assert(!(x < 0) && !(x > 0) && !(x == 0));
1790         x = 1;
1791         assert(x > 0 && !(x < 0) && !(x == 0));
1792     }
1793 
1794     /**
1795     Defines hooks for unary operators `-`, `~`, `++`, and `--`.
1796 
1797     For `-` and `~`, if $(D v == WithNaN.defaultValue!T), returns
1798     `WithNaN.defaultValue!T`. Otherwise, the semantics is the same as for the
1799     built-in operator.
1800 
1801     For `++` and `--`, if $(D v == WithNaN.defaultValue!Lhs) or the operation
1802     would result in an overflow, sets `v` to `WithNaN.defaultValue!T`.
1803     Otherwise, the semantics is the same as for the built-in operator.
1804 
1805     Params:
1806     x = The operator symbol
1807     v = The left-hand side of the comparison (`T` is the first argument to
1808     `Checked`)
1809 
1810     Returns: $(UL $(LI For $(D x == "-" || x == "~"): If  $(D v ==
1811     WithNaN.defaultValue!T), the function returns `WithNaN.defaultValue!T`.
1812     Otherwise it returns the normal result of the operator.) $(LI For $(D x ==
1813     "++" || x == "--"): The function returns `void`.))
1814 
1815     */
1816     auto hookOpUnary(string x, T)(ref T v)
1817     {
1818         static if (x == "-" || x == "~")
1819         {
1820             return v != defaultValue!T ? mixin(x ~ "v") : v;
1821         }
1822         else static if (x == "++")
1823         {
1824             static if (defaultValue!T == T.min)
1825             {
1826                 if (v != defaultValue!T)
1827                 {
1828                     if (v == T.max) v = defaultValue!T;
1829                     else ++v;
1830                 }
1831             }
1832             else
1833             {
1834                 static assert(defaultValue!T == T.max);
1835                 if (v != defaultValue!T) ++v;
1836             }
1837         }
1838         else static if (x == "--")
1839         {
1840             if (v != defaultValue!T) --v;
1841         }
1842     }
1843 
1844     ///
1845     unittest
1846     {
1847         Checked!(int, WithNaN) x;
1848         ++x;
1849         assert(x.isNaN);
1850         x = 1;
1851         assert(!x.isNaN);
1852         x = -x;
1853         ++x;
1854         assert(!x.isNaN);
1855     }
1856 
1857     unittest // for coverage
1858     {
1859         Checked!(uint, WithNaN) y;
1860         ++y;
1861         assert(y.isNaN);
1862     }
1863 
1864     /**
1865     Defines hooks for binary operators `+`, `-`, `*`, `/`, `%`, `^^`, `&`, `|`,
1866      `^`, `<<`, `>>`, and `>>>` for cases where a `Checked` object is the
1867     left-hand side operand. If $(D lhs == WithNaN.defaultValue!Lhs), returns
1868     $(D WithNaN.defaultValue!(typeof(lhs + rhs))) without evaluating the
1869     operand. Otherwise, evaluates the operand. If evaluation does not overflow,
1870     returns the result. Otherwise, returns $(D WithNaN.defaultValue!(typeof(lhs
1871     + rhs))).
1872 
1873     Params:
1874     x = The operator symbol
1875     lhs = The left-hand side operand (`Lhs` is the first argument to `Checked`)
1876     rhs = The right-hand side operand
1877 
1878     Returns: If $(D lhs != WithNaN.defaultValue!Lhs) and the operator does not
1879     overflow, the function returns the same result as the built-in operator. In
1880     all other cases, returns $(D WithNaN.defaultValue!(typeof(lhs + rhs))).
1881     */
1882     auto hookOpBinary(string x, L, R)(L lhs, R rhs)
1883     {
1884         alias Result = typeof(lhs + rhs);
1885         if (lhs != defaultValue!L)
1886         {
1887             bool error;
1888             auto result = opChecked!x(lhs, rhs, error);
1889             if (!error) return result;
1890         }
1891         return defaultValue!Result;
1892     }
1893 
1894     ///
1895     unittest
1896     {
1897         Checked!(int, WithNaN) x;
1898         assert((x + 1).isNaN);
1899         x = 100;
1900         assert(!(x + 1).isNaN);
1901     }
1902 
1903     /**
1904     Defines hooks for binary operators `+`, `-`, `*`, `/`, `%`, `^^`, `&`, `|`,
1905      `^`, `<<`, `>>`, and `>>>` for cases where a `Checked` object is the
1906     right-hand side operand. If $(D rhs == WithNaN.defaultValue!Rhs), returns
1907     $(D WithNaN.defaultValue!(typeof(lhs + rhs))) without evaluating the
1908     operand. Otherwise, evaluates the operand. If evaluation does not overflow,
1909     returns the result. Otherwise, returns $(D WithNaN.defaultValue!(typeof(lhs
1910     + rhs))).
1911 
1912     Params:
1913     x = The operator symbol
1914     lhs = The left-hand side operand
1915     rhs = The right-hand side operand (`Rhs` is the first argument to `Checked`)
1916 
1917     Returns: If $(D rhs != WithNaN.defaultValue!Rhs) and the operator does not
1918     overflow, the function returns the same result as the built-in operator. In
1919     all other cases, returns $(D WithNaN.defaultValue!(typeof(lhs + rhs))).
1920     */
1921     auto hookOpBinaryRight(string x, L, R)(L lhs, R rhs)
1922     {
1923         alias Result = typeof(lhs + rhs);
1924         if (rhs != defaultValue!R)
1925         {
1926             bool error;
1927             auto result = opChecked!x(lhs, rhs, error);
1928             if (!error) return result;
1929         }
1930         return defaultValue!Result;
1931     }
1932     ///
1933     unittest
1934     {
1935         Checked!(int, WithNaN) x;
1936         assert((1 + x).isNaN);
1937         x = 100;
1938         assert(!(1 + x).isNaN);
1939     }
1940 
1941     /**
1942 
1943     Defines hooks for binary operators `+=`, `-=`, `*=`, `/=`, `%=`, `^^=`,
1944     `&=`, `|=`, `^=`, `<<=`, `>>=`, and `>>>=` for cases where a `Checked`
1945     object is the left-hand side operand. If $(D lhs ==
1946     WithNaN.defaultValue!Lhs), no action is carried. Otherwise, evaluates the
1947     operand. If evaluation does not overflow and fits in `Lhs` without loss of
1948     information or change of sign, sets `lhs` to the result. Otherwise, sets
1949     `lhs` to `WithNaN.defaultValue!Lhs`.
1950 
1951     Params:
1952     x = The operator symbol (without the `=`)
1953     lhs = The left-hand side operand (`Lhs` is the first argument to `Checked`)
1954     rhs = The right-hand side operand
1955 
1956     Returns: `void`
1957     */
1958     void hookOpOpAssign(string x, L, R)(ref L lhs, R rhs)
1959     {
1960         if (lhs == defaultValue!L)
1961             return;
1962         bool error;
1963         auto temp = opChecked!x(lhs, rhs, error);
1964         lhs = error
1965             ? defaultValue!L
1966             : hookOpCast!L(temp);
1967     }
1968 
1969     ///
1970     unittest
1971     {
1972         Checked!(int, WithNaN) x;
1973         x += 4;
1974         assert(x.isNaN);
1975         x = 0;
1976         x += 4;
1977         assert(!x.isNaN);
1978         x += int.max;
1979         assert(x.isNaN);
1980     }
1981 }
1982 
1983 ///
1984 unittest
1985 {
1986     auto x1 = Checked!(int, WithNaN)();
1987     assert(x1.isNaN);
1988     assert(x1.get == int.min);
1989     assert(x1 != x1);
1990     assert(!(x1 < x1));
1991     assert(!(x1 > x1));
1992     assert(!(x1 == x1));
1993     ++x1;
1994     assert(x1.isNaN);
1995     assert(x1.get == int.min);
1996     --x1;
1997     assert(x1.isNaN);
1998     assert(x1.get == int.min);
1999     x1 = 42;
2000     assert(!x1.isNaN);
2001     assert(x1 == x1);
2002     assert(x1 <= x1);
2003     assert(x1 >= x1);
2004     static assert(x1.min == int.min + 1);
2005     x1 += long(int.max);
2006 }
2007 
2008 /**
2009 Queries whether a $(D Checked!(T, WithNaN)) object is not a number (NaN).
2010 
2011 Params: x = the `Checked` instance queried
2012 
2013 Returns: `true` if `x` is a NaN, `false` otherwise
2014 */
2015 bool isNaN(T)(const Checked!(T, WithNaN) x)
2016 {
2017     return x.get == x.init.get;
2018 }
2019 
2020 ///
2021 unittest
2022 {
2023     auto x1 = Checked!(int, WithNaN)();
2024     assert(x1.isNaN);
2025     x1 = 1;
2026     assert(!x1.isNaN);
2027     x1 = x1.init;
2028     assert(x1.isNaN);
2029 }
2030 
2031 unittest
2032 {
2033     void test1(T)()
2034     {
2035         auto x1 = Checked!(T, WithNaN)();
2036         assert(x1.isNaN);
2037         assert(x1.get == int.min);
2038         assert(x1 != x1);
2039         assert(!(x1 < x1));
2040         assert(!(x1 > x1));
2041         assert(!(x1 == x1));
2042         assert(x1.get == int.min);
2043         auto x2 = Checked!(T, WithNaN)(42);
2044         assert(!x2.isNaN);
2045         assert(x2 == x2);
2046         assert(x2 <= x2);
2047         assert(x2 >= x2);
2048         static assert(x2.min == T.min + 1);
2049     }
2050     test1!int;
2051     test1!(const int);
2052     test1!(immutable int);
2053 
2054     void test2(T)()
2055     {
2056         auto x1 = Checked!(T, WithNaN)();
2057         assert(x1.get == T.min);
2058         assert(x1 != x1);
2059         assert(!(x1 < x1));
2060         assert(!(x1 > x1));
2061         assert(!(x1 == x1));
2062         ++x1;
2063         assert(x1.get == T.min);
2064         --x1;
2065         assert(x1.get == T.min);
2066         x1 = 42;
2067         assert(x1 == x1);
2068         assert(x1 <= x1);
2069         assert(x1 >= x1);
2070         static assert(x1.min == T.min + 1);
2071         x1 += long(T.max);
2072     }
2073     test2!int;
2074 }
2075 
2076 unittest
2077 {
2078     alias Smart(T) = Checked!(Checked!(T, ProperCompare), WithNaN);
2079     Smart!int x1;
2080     assert(x1 != x1);
2081     x1 = -1;
2082     assert(x1 < 1u);
2083     auto x2 = Smart!(const int)(42);
2084 }
2085 
2086 // Saturate
2087 /**
2088 
2089 Hook that implements $(I saturation), i.e. any arithmetic operation that would
2090 overflow leaves the result at its extreme value (`min` or `max` depending on the
2091 direction of the overflow).
2092 
2093 Saturation is not sticky; if a value reaches its saturation value, another
2094 operation may take it back to normal range.
2095 
2096 */
2097 struct Saturate
2098 {
2099 static:
2100     /**
2101 
2102     Implements saturation for operators `+=`, `-=`, `*=`, `/=`, `%=`, `^^=`, `&=`, `|=`, `^=`, `<<=`, `>>=`,
2103     and `>>>=`. This hook is called if the result of the binary operation does
2104     not fit in `Lhs` without loss of information or a change in sign.
2105 
2106     Params:
2107     Rhs = The right-hand side type in the assignment, after the operation has
2108     been computed
2109     bound = The bound being violated
2110 
2111     Returns: `Lhs.max` if $(D rhs >= 0), `Lhs.min` otherwise.
2112 
2113     */
2114     T onLowerBound(Rhs, T)(Rhs rhs, T bound)
2115     {
2116         return bound;
2117     }
2118     /// ditto
2119     T onUpperBound(Rhs, T)(Rhs rhs, T bound)
2120     {
2121         return bound;
2122     }
2123     ///
2124     unittest
2125     {
2126         auto x = checked!Saturate(short(100));
2127         x += 33000;
2128         assert(x == short.max);
2129         x -= 70000;
2130         assert(x == short.min);
2131     }
2132 
2133     /**
2134 
2135     Implements saturation for operators `+`, `-` (unary and binary), `*`, `/`,
2136     `%`, `^^`, `&`, `|`, `^`, `<<`, `>>`, and `>>>`.
2137 
2138     For unary `-`, `onOverflow` is called if $(D lhs == Lhs.min) and `Lhs` is a
2139     signed type. The function returns `Lhs.max`.
2140 
2141     For binary operators, the result is as follows: $(UL $(LI `Lhs.max` if the
2142     result overflows in the positive direction, on division by `0`, or on
2143     shifting right by a negative value) $(LI `Lhs.min` if the result overflows
2144     in the negative direction) $(LI `0` if `lhs` is being shifted left by a
2145     negative value, or shifted right by a large positive value))
2146 
2147     Params:
2148     x = The operator involved in the `opAssign` operation
2149     Lhs = The left-hand side of the operator (`Lhs` is the first argument to
2150     `Checked`)
2151     Rhs = The right-hand side type in the operator
2152 
2153     Returns: The saturated result of the operator.
2154 
2155     */
2156     typeof(~Lhs()) onOverflow(string x, Lhs)(Lhs lhs)
2157     {
2158         static assert(x == "-" || x == "++" || x == "--");
2159         return x == "--" ? Lhs.min : Lhs.max;
2160     }
2161     /// ditto
2162     typeof(Lhs() + Rhs()) onOverflow(string x, Lhs, Rhs)(Lhs lhs, Rhs rhs)
2163     {
2164         static if (x == "+")
2165             return rhs >= 0 ? Lhs.max : Lhs.min;
2166         else static if (x == "*")
2167             return (lhs >= 0) == (rhs >= 0) ? Lhs.max : Lhs.min;
2168         else static if (x == "^^")
2169             return lhs > 0 || !(rhs & 1) ? Lhs.max : Lhs.min;
2170         else static if (x == "-")
2171             return rhs >= 0 ? Lhs.min : Lhs.max;
2172         else static if (x == "/" || x == "%")
2173             return Lhs.max;
2174         else static if (x == "<<")
2175             return rhs >= 0 ? Lhs.max : 0;
2176         else static if (x == ">>" || x == ">>>")
2177             return rhs >= 0 ? 0 : Lhs.max;
2178         else
2179             static assert(false);
2180     }
2181     ///
2182     unittest
2183     {
2184         assert(checked!Saturate(int.max) + 1 == int.max);
2185         assert(checked!Saturate(100) ^^ 10 == int.max);
2186         assert(checked!Saturate(-100) ^^ 10 == int.max);
2187         assert(checked!Saturate(100) / 0 == int.max);
2188         assert(checked!Saturate(100) << -1 == 0);
2189         assert(checked!Saturate(100) << 33 == int.max);
2190         assert(checked!Saturate(100) >> -1 == int.max);
2191         assert(checked!Saturate(100) >> 33 == 0);
2192     }
2193 }
2194 
2195 ///
2196 unittest
2197 {
2198     auto x = checked!Saturate(int.max);
2199     ++x;
2200     assert(x == int.max);
2201     --x;
2202     assert(x == int.max - 1);
2203     x = int.min;
2204     assert(-x == int.max);
2205     x -= 42;
2206     assert(x == int.min);
2207     assert(x * -2 == int.max);
2208 }
2209 
2210 /*
2211 Yields `true` if `T1` is "value convertible" (by C's "value preserving" rule,
2212 see $(HTTP c-faq.com/expr/preservingrules.html)) to `T2`, where the two are
2213 integral types. That is, all of values in `T1` are also in `T2`. For example
2214 `int` is value convertible to `long` but not to `uint` or `ulong`.
2215 */
2216 private enum valueConvertible(T1, T2) = isIntegral!T1 && isIntegral!T2 &&
2217     is(T1 : T2) && (
2218         isUnsigned!T1 == isUnsigned!T2 || // same signedness
2219         !isUnsigned!T2 && T2.sizeof > T1.sizeof // safely convertible
2220     );
2221 
2222 /**
2223 
2224 Defines binary operations with overflow checking for any two integral types.
2225 The result type obeys the language rules (even when they may be
2226 counterintuitive), and `overflow` is set if an overflow occurs (including
2227 inadvertent change of signedness, e.g. `-1` is converted to `uint`).
2228 Conceptually the behavior is:
2229 
2230 $(OL $(LI Perform the operation in infinite precision)
2231 $(LI If the infinite-precision result fits in the result type, return it and
2232 do not touch `overflow`)
2233 $(LI Otherwise, set `overflow` to `true` and return an unspecified value)
2234 )
2235 
2236 The implementation exploits properties of types and operations to minimize
2237 additional work.
2238 
2239 Params:
2240 x = The binary operator involved, e.g. `/`
2241 lhs = The left-hand side of the operator
2242 rhs = The right-hand side of the operator
2243 error = The error indicator (assigned `true` in case there's an error)
2244 
2245 Returns:
2246 The result of the operation, which is the same as the built-in operator
2247 */
2248 typeof(mixin(x == "cmp" ? "0" : ("L() " ~ x ~ " R()")))
2249 opChecked(string x, L, R)(const L lhs, const R rhs, ref bool error)
2250 if (isIntegral!L && isIntegral!R)
2251 {
2252     static if (x == "cmp")
2253         alias Result = int;
2254     else
2255         alias Result = typeof(mixin("L() " ~ x ~ " R()"));
2256 
2257     import core.checkedint;
2258     import std.algorithm.comparison : among;
2259     static if (x == "==")
2260     {
2261         alias C = typeof(lhs + rhs);
2262         static if (valueConvertible!(L, C) && valueConvertible!(R, C))
2263         {
2264             // Values are converted to R before comparison, cool.
2265             return lhs == rhs;
2266         }
2267         else
2268         {
2269             static assert(isUnsigned!C);
2270             static assert(isUnsigned!L != isUnsigned!R);
2271             if (lhs != rhs) return false;
2272             // R(lhs) and R(rhs) have the same bit pattern, yet may be
2273             // different due to signedness change.
2274             static if (!isUnsigned!R)
2275             {
2276                 if (rhs >= 0)
2277                     return true;
2278             }
2279             else
2280             {
2281                 if (lhs >= 0)
2282                     return true;
2283             }
2284             error = true;
2285             return true;
2286         }
2287     }
2288     else static if (x == "cmp")
2289     {
2290         alias C = typeof(lhs + rhs);
2291         static if (!valueConvertible!(L, C) || !valueConvertible!(R, C))
2292         {
2293             static assert(isUnsigned!C);
2294             static assert(isUnsigned!L != isUnsigned!R);
2295             if (!isUnsigned!L && lhs < 0)
2296             {
2297                 error = true;
2298                 return -1;
2299             }
2300             if (!isUnsigned!R && rhs < 0)
2301             {
2302                 error = true;
2303                 return 1;
2304             }
2305         }
2306         return lhs < rhs ? -1 : lhs > rhs;
2307     }
2308     else static if (x.among("<<", ">>", ">>>"))
2309     {
2310         // Handle shift separately from all others. The test below covers
2311         // negative rhs as well.
2312         import std.conv : unsigned;
2313         if (unsigned(rhs) > 8 * Result.sizeof) goto fail;
2314         return mixin("lhs" ~ x ~ "rhs");
2315     }
2316     else static if (x.among("&", "|", "^"))
2317     {
2318         // Nothing to check
2319         return mixin("lhs" ~ x ~ "rhs");
2320     }
2321     else static if (x == "^^")
2322     {
2323         // Exponentiation is weird, handle separately
2324         return pow(lhs, rhs, error);
2325     }
2326     else static if (valueConvertible!(L, Result) &&
2327             valueConvertible!(R, Result))
2328     {
2329         static if (L.sizeof < Result.sizeof && R.sizeof < Result.sizeof &&
2330             x.among("+", "-", "*"))
2331         {
2332             // No checks - both are value converted and result is in range
2333             return mixin("lhs" ~ x ~ "rhs");
2334         }
2335         else static if (x == "+")
2336         {
2337             static if (isUnsigned!Result) alias impl = addu;
2338             else alias impl = adds;
2339             return impl(Result(lhs), Result(rhs), error);
2340         }
2341         else static if (x == "-")
2342         {
2343             static if (isUnsigned!Result) alias impl = subu;
2344             else alias impl = subs;
2345             return impl(Result(lhs), Result(rhs), error);
2346         }
2347         else static if (x == "*")
2348         {
2349             static if (!isUnsigned!L && !isUnsigned!R &&
2350                 is(L == Result))
2351             {
2352                 if (lhs == Result.min && rhs == -1) goto fail;
2353             }
2354             static if (isUnsigned!Result) alias impl = mulu;
2355             else alias impl = muls;
2356             return impl(Result(lhs), Result(rhs), error);
2357         }
2358         else static if (x == "/" || x == "%")
2359         {
2360             static if (!isUnsigned!L && !isUnsigned!R &&
2361                 is(L == Result) && x == "/")
2362             {
2363                 if (lhs == Result.min && rhs == -1) goto fail;
2364             }
2365             if (rhs == 0) goto fail;
2366             return mixin("lhs" ~ x ~ "rhs");
2367         }
2368         else static assert(0, x);
2369     }
2370     else // Mixed signs
2371     {
2372         static assert(isUnsigned!Result);
2373         static assert(isUnsigned!L != isUnsigned!R);
2374         static if (x == "+")
2375         {
2376             static if (!isUnsigned!L)
2377             {
2378                 if (lhs < 0)
2379                     return subu(Result(rhs), Result(-lhs), error);
2380             }
2381             else static if (!isUnsigned!R)
2382             {
2383                 if (rhs < 0)
2384                     return subu(Result(lhs), Result(-rhs), error);
2385             }
2386             return addu(Result(lhs), Result(rhs), error);
2387         }
2388         else static if (x == "-")
2389         {
2390             static if (!isUnsigned!L)
2391             {
2392                 if (lhs < 0) goto fail;
2393             }
2394             else static if (!isUnsigned!R)
2395             {
2396                 if (rhs < 0)
2397                     return addu(Result(lhs), Result(-rhs), error);
2398             }
2399             return subu(Result(lhs), Result(rhs), error);
2400         }
2401         else static if (x == "*")
2402         {
2403             static if (!isUnsigned!L)
2404             {
2405                 if (lhs < 0) goto fail;
2406             }
2407             else static if (!isUnsigned!R)
2408             {
2409                 if (rhs < 0) goto fail;
2410             }
2411             return mulu(Result(lhs), Result(rhs), error);
2412         }
2413         else static if (x == "/" || x == "%")
2414         {
2415             static if (!isUnsigned!L)
2416             {
2417                 if (lhs < 0 || rhs == 0) goto fail;
2418             }
2419             else static if (!isUnsigned!R)
2420             {
2421                 if (rhs <= 0) goto fail;
2422             }
2423             return mixin("Result(lhs)" ~ x ~ "Result(rhs)");
2424         }
2425         else static assert(0, x);
2426     }
2427     debug assert(false);
2428 fail:
2429     error = true;
2430     return Result(0);
2431 }
2432 
2433 ///
2434 unittest
2435 {
2436     bool overflow;
2437     assert(opChecked!"+"(const short(1), short(1), overflow) == 2 && !overflow);
2438     assert(opChecked!"+"(1, 1, overflow) == 2 && !overflow);
2439     assert(opChecked!"+"(1, 1u, overflow) == 2 && !overflow);
2440     assert(opChecked!"+"(-1, 1u, overflow) == 0 && !overflow);
2441     assert(opChecked!"+"(1u, -1, overflow) == 0 && !overflow);
2442 }
2443 
2444 ///
2445 unittest
2446 {
2447     bool overflow;
2448     assert(opChecked!"-"(1, 1, overflow) == 0 && !overflow);
2449     assert(opChecked!"-"(1, 1u, overflow) == 0 && !overflow);
2450     assert(opChecked!"-"(1u, -1, overflow) == 2 && !overflow);
2451     assert(opChecked!"-"(-1, 1u, overflow) == 0 && overflow);
2452 }
2453 
2454 unittest
2455 {
2456     bool overflow;
2457     assert(opChecked!"*"(2, 3, overflow) == 6 && !overflow);
2458     assert(opChecked!"*"(2, 3u, overflow) == 6 && !overflow);
2459     assert(opChecked!"*"(1u, -1, overflow) == 0 && overflow);
2460     //assert(mul(-1, 1u, overflow) == uint.max - 1 && overflow);
2461 }
2462 
2463 unittest
2464 {
2465     bool overflow;
2466     assert(opChecked!"/"(6, 3, overflow) == 2 && !overflow);
2467     assert(opChecked!"/"(6, 3, overflow) == 2 && !overflow);
2468     assert(opChecked!"/"(6u, 3, overflow) == 2 && !overflow);
2469     assert(opChecked!"/"(6, 3u, overflow) == 2 && !overflow);
2470     assert(opChecked!"/"(11, 0, overflow) == 0 && overflow);
2471     overflow = false;
2472     assert(opChecked!"/"(6u, 0, overflow) == 0 && overflow);
2473     overflow = false;
2474     assert(opChecked!"/"(-6, 2u, overflow) == 0 && overflow);
2475     overflow = false;
2476     assert(opChecked!"/"(-6, 0u, overflow) == 0 && overflow);
2477     overflow = false;
2478     assert(opChecked!"cmp"(0u, -6, overflow) == 1 && overflow);
2479     overflow = false;
2480     assert(opChecked!"|"(1, 2, overflow) == 3 && !overflow);
2481 }
2482 
2483 /*
2484 Exponentiation function used by the implementation of operator `^^`.
2485 */
2486 private pure @safe nothrow @nogc
2487 auto pow(L, R)(const L lhs, const R rhs, ref bool overflow)
2488 if (isIntegral!L && isIntegral!R)
2489 {
2490     if (rhs <= 1)
2491     {
2492         if (rhs == 0) return 1;
2493         static if (!isUnsigned!R)
2494             return rhs == 1
2495                 ? lhs
2496                 : (rhs == -1 && (lhs == 1 || lhs == -1)) ? lhs : 0;
2497         else
2498             return lhs;
2499     }
2500 
2501     typeof(lhs ^^ rhs) b = void;
2502     static if (!isUnsigned!L && isUnsigned!(typeof(b)))
2503     {
2504         // Need to worry about mixed-sign stuff
2505         if (lhs < 0)
2506         {
2507             if (rhs & 1)
2508             {
2509                 if (lhs < 0) overflow = true;
2510                 return 0;
2511             }
2512             b = -lhs;
2513         }
2514         else
2515         {
2516             b = lhs;
2517         }
2518     }
2519     else
2520     {
2521         b = lhs;
2522     }
2523     if (b == 1) return 1;
2524     if (b == -1) return (rhs & 1) ? -1 : 1;
2525     if (rhs > 63)
2526     {
2527         overflow = true;
2528         return 0;
2529     }
2530 
2531     assert((b > 1 || b < -1) && rhs > 1);
2532     return powImpl(b, cast(uint) rhs, overflow);
2533 }
2534 
2535 // Inspiration: http://www.stepanovpapers.com/PAM.pdf
2536 pure @safe nothrow @nogc
2537 private T powImpl(T)(T b, uint e, ref bool overflow)
2538 if (isIntegral!T && T.sizeof >= 4)
2539 {
2540     assert(e > 1);
2541 
2542     import core.checkedint : muls, mulu;
2543     static if (isUnsigned!T) alias mul = mulu;
2544     else alias mul = muls;
2545 
2546     T r = b;
2547     --e;
2548     // Loop invariant: r * (b ^^ e) is the actual result
2549     for (;; e /= 2)
2550     {
2551         if (e % 2)
2552         {
2553             r = mul(r, b, overflow);
2554             if (e == 1) break;
2555         }
2556         b = mul(b, b, overflow);
2557     }
2558     return r;
2559 }
2560 
2561 unittest
2562 {
2563     static void testPow(T)(T x, uint e)
2564     {
2565         bool overflow;
2566         assert(opChecked!"^^"(T(0), 0, overflow) == 1);
2567         assert(opChecked!"^^"(-2, T(0), overflow) == 1);
2568         assert(opChecked!"^^"(-2, T(1), overflow) == -2);
2569         assert(opChecked!"^^"(-1, -1, overflow) == -1);
2570         assert(opChecked!"^^"(-2, 1, overflow) == -2);
2571         assert(opChecked!"^^"(-2, -1, overflow) == 0);
2572         assert(opChecked!"^^"(-2, 4u, overflow) == 16);
2573         assert(!overflow);
2574         assert(opChecked!"^^"(-2, 3u, overflow) == 0);
2575         assert(overflow);
2576         overflow = false;
2577         assert(opChecked!"^^"(3, 64u, overflow) == 0);
2578         assert(overflow);
2579         overflow = false;
2580         foreach (uint i; 0 .. e)
2581         {
2582             assert(opChecked!"^^"(x, i, overflow) == x ^^ i);
2583             assert(!overflow);
2584         }
2585         assert(opChecked!"^^"(x, e, overflow) == x ^^ e);
2586         assert(overflow);
2587     }
2588 
2589     testPow!int(3, 21);
2590     testPow!uint(3, 21);
2591     testPow!long(3, 40);
2592     testPow!ulong(3, 41);
2593 }
2594 
2595 version(unittest) private struct CountOverflows
2596 {
2597     uint calls;
2598     auto onOverflow(string op, Lhs)(Lhs lhs)
2599     {
2600         ++calls;
2601         return mixin(op ~ "lhs");
2602     }
2603     auto onOverflow(string op, Lhs, Rhs)(Lhs lhs, Rhs rhs)
2604     {
2605         ++calls;
2606         return mixin("lhs" ~ op ~ "rhs");
2607     }
2608     T onLowerBound(Rhs, T)(Rhs rhs, T bound)
2609     {
2610         ++calls;
2611         return cast(T) rhs;
2612     }
2613     T onUpperBound(Rhs, T)(Rhs rhs, T bound)
2614     {
2615         ++calls;
2616         return cast(T) rhs;
2617     }
2618 }
2619 
2620 version(unittest) private struct CountOpBinary
2621 {
2622     uint calls;
2623     auto hookOpBinary(string op, Lhs, Rhs)(Lhs lhs, Rhs rhs)
2624     {
2625         ++calls;
2626         return mixin("lhs" ~ op ~ "rhs");
2627     }
2628 }
2629 
2630 // opBinary
2631 @nogc nothrow pure @safe unittest
2632 {
2633     auto x = Checked!(const int, void)(42), y = Checked!(immutable int, void)(142);
2634     assert(x + y == 184);
2635     assert(x + 100 == 142);
2636     assert(y - x == 100);
2637     assert(200 - x == 158);
2638     assert(y * x == 142 * 42);
2639     assert(x / 1 == 42);
2640     assert(x % 20 == 2);
2641 
2642     auto x1 = Checked!(int, CountOverflows)(42);
2643     assert(x1 + 0 == 42);
2644     assert(x1 + false == 42);
2645     assert(is(typeof(x1 + 0.5) == double));
2646     assert(x1 + 0.5 == 42.5);
2647     assert(x1.hook.calls == 0);
2648     assert(x1 + int.max == int.max + 42);
2649     assert(x1.hook.calls == 1);
2650     assert(x1 * 2 == 84);
2651     assert(x1.hook.calls == 1);
2652     assert(x1 / 2 == 21);
2653     assert(x1.hook.calls == 1);
2654     assert(x1 % 20 == 2);
2655     assert(x1.hook.calls == 1);
2656     assert(x1 << 2 == 42 << 2);
2657     assert(x1.hook.calls == 1);
2658     assert(x1 << 42 == x1.get << x1.get);
2659     assert(x1.hook.calls == 2);
2660     x1 = int.min;
2661     assert(x1 - 1 == int.max);
2662     assert(x1.hook.calls == 3);
2663 
2664     auto x2 = Checked!(int, CountOpBinary)(42);
2665     assert(x2 + 1 == 43);
2666     assert(x2.hook.calls == 1);
2667 
2668     auto x3 = Checked!(uint, CountOverflows)(42u);
2669     assert(x3 + 1 == 43);
2670     assert(x3.hook.calls == 0);
2671     assert(x3 - 1 == 41);
2672     assert(x3.hook.calls == 0);
2673     assert(x3 + (-42) == 0);
2674     assert(x3.hook.calls == 0);
2675     assert(x3 - (-42) == 84);
2676     assert(x3.hook.calls == 0);
2677     assert(x3 * 2 == 84);
2678     assert(x3.hook.calls == 0);
2679     assert(x3 * -2 == -84);
2680     assert(x3.hook.calls == 1);
2681     assert(x3 / 2 == 21);
2682     assert(x3.hook.calls == 1);
2683     assert(x3 / -2 == 0);
2684     assert(x3.hook.calls == 2);
2685     assert(x3 ^^ 2 == 42 * 42);
2686     assert(x3.hook.calls == 2);
2687 
2688     auto x4 = Checked!(int, CountOverflows)(42);
2689     assert(x4 + 1 == 43);
2690     assert(x4.hook.calls == 0);
2691     assert(x4 + 1u == 43);
2692     assert(x4.hook.calls == 0);
2693     assert(x4 - 1 == 41);
2694     assert(x4.hook.calls == 0);
2695     assert(x4 * 2 == 84);
2696     assert(x4.hook.calls == 0);
2697     x4 = -2;
2698     assert(x4 + 2u == 0);
2699     assert(x4.hook.calls == 0);
2700     assert(x4 * 2u == -4);
2701     assert(x4.hook.calls == 1);
2702 
2703     auto x5 = Checked!(int, CountOverflows)(3);
2704     assert(x5 ^^ 0 == 1);
2705     assert(x5 ^^ 1 == 3);
2706     assert(x5 ^^ 2 == 9);
2707     assert(x5 ^^ 3 == 27);
2708     assert(x5 ^^ 4 == 81);
2709     assert(x5 ^^ 5 == 81 * 3);
2710     assert(x5 ^^ 6 == 81 * 9);
2711 }
2712 
2713 // opBinaryRight
2714 @nogc nothrow pure @safe unittest
2715 {
2716     auto x1 = Checked!(int, CountOverflows)(42);
2717     assert(1 + x1 == 43);
2718     assert(true + x1 == 43);
2719     assert(0.5 + x1 == 42.5);
2720     auto x2 = Checked!(int, void)(42);
2721     assert(x1 + x2 == 84);
2722     assert(x2 + x1   == 84);
2723 }
2724 
2725 // opOpAssign
2726 unittest
2727 {
2728     auto x1 = Checked!(int, CountOverflows)(3);
2729     assert((x1 += 2) == 5);
2730     x1 *= 2_000_000_000L;
2731     assert(x1.hook.calls == 1);
2732     x1 *= -2_000_000_000L;
2733     assert(x1.hook.calls == 2);
2734 
2735     auto x2 = Checked!(ushort, CountOverflows)(ushort(3));
2736     assert((x2 += 2) == 5);
2737     assert(x2.hook.calls == 0);
2738     assert((x2 += ushort.max) == cast(ushort) (ushort(5) + ushort.max));
2739     assert(x2.hook.calls == 1);
2740 
2741     auto x3 = Checked!(uint, CountOverflows)(3u);
2742     x3 *= ulong(2_000_000_000);
2743     assert(x3.hook.calls == 1);
2744 }
2745 
2746 // opAssign
2747 unittest
2748 {
2749     Checked!(int, void) x;
2750     x = 42;
2751     assert(x.get == 42);
2752     x = x;
2753     assert(x.get == 42);
2754     x = short(43);
2755     assert(x.get == 43);
2756     x = ushort(44);
2757     assert(x.get == 44);
2758 }
2759 
2760 unittest
2761 {
2762     static assert(!is(typeof(Checked!(short, void)(ushort(42)))));
2763     static assert(!is(typeof(Checked!(int, void)(long(42)))));
2764     static assert(!is(typeof(Checked!(int, void)(ulong(42)))));
2765     assert(Checked!(short, void)(short(42)).get == 42);
2766     assert(Checked!(int, void)(ushort(42)).get == 42);
2767 }
2768 
2769 // opCast
2770 @nogc nothrow pure @safe unittest
2771 {
2772     static assert(is(typeof(cast(float) Checked!(int, void)(42)) == float));
2773     assert(cast(float) Checked!(int, void)(42) == 42);
2774 
2775     assert(is(typeof(cast(long) Checked!(int, void)(42)) == long));
2776     assert(cast(long) Checked!(int, void)(42) == 42);
2777     static assert(is(typeof(cast(long) Checked!(uint, void)(42u)) == long));
2778     assert(cast(long) Checked!(uint, void)(42u) == 42);
2779 
2780     auto x = Checked!(int, void)(42);
2781     if (x) {} else assert(0);
2782     x = 0;
2783     if (x) assert(0);
2784 
2785     static struct Hook1
2786     {
2787         uint calls;
2788         Dst hookOpCast(Dst, Src)(Src value)
2789         {
2790             ++calls;
2791             return 42;
2792         }
2793     }
2794     auto y = Checked!(long, Hook1)(long.max);
2795     assert(cast(int) y == 42);
2796     assert(cast(uint) y == 42);
2797     assert(y.hook.calls == 2);
2798 
2799     static struct Hook2
2800     {
2801         uint calls;
2802         Dst onBadCast(Dst, Src)(Src value)
2803         {
2804             ++calls;
2805             return 42;
2806         }
2807     }
2808     auto x1 = Checked!(uint, Hook2)(100u);
2809     assert(cast(ushort) x1 == 100);
2810     assert(cast(short) x1 == 100);
2811     assert(cast(float) x1 == 100);
2812     assert(cast(double) x1 == 100);
2813     assert(cast(real) x1 == 100);
2814     assert(x1.hook.calls == 0);
2815     assert(cast(int) x1 == 100);
2816     assert(x1.hook.calls == 0);
2817     x1 = uint.max;
2818     assert(cast(int) x1 == 42);
2819     assert(x1.hook.calls == 1);
2820 
2821     auto x2 = Checked!(int, Hook2)(-100);
2822     assert(cast(short) x2 == -100);
2823     assert(cast(ushort) x2 == 42);
2824     assert(cast(uint) x2 == 42);
2825     assert(cast(ulong) x2 == 42);
2826     assert(x2.hook.calls == 3);
2827 }
2828 
2829 // opEquals
2830 @nogc nothrow pure @safe unittest
2831 {
2832     assert(Checked!(int, void)(42) == 42L);
2833     assert(42UL == Checked!(int, void)(42));
2834 
2835     static struct Hook1
2836     {
2837         uint calls;
2838         bool hookOpEquals(Lhs, Rhs)(const Lhs lhs, const Rhs rhs)
2839         {
2840             ++calls;
2841             return lhs != rhs;
2842         }
2843     }
2844     auto x1 = Checked!(int, Hook1)(100);
2845     assert(x1 != Checked!(long, Hook1)(100));
2846     assert(x1.hook.calls == 1);
2847     assert(x1 != 100u);
2848     assert(x1.hook.calls == 2);
2849 
2850     static struct Hook2
2851     {
2852         uint calls;
2853         bool hookOpEquals(Lhs, Rhs)(Lhs lhs, Rhs rhs)
2854         {
2855             ++calls;
2856             return false;
2857         }
2858     }
2859     auto x2 = Checked!(int, Hook2)(-100);
2860     assert(x2 != x1);
2861     // For coverage: lhs has no hookOpEquals, rhs does
2862     assert(Checked!(uint, void)(100u) != x2);
2863     // For coverage: different types, neither has a hookOpEquals
2864     assert(Checked!(uint, void)(100u) == Checked!(int, void*)(100));
2865     assert(x2.hook.calls == 0);
2866     assert(x2 != -100);
2867     assert(x2.hook.calls == 1);
2868     assert(x2 != cast(uint) -100);
2869     assert(x2.hook.calls == 2);
2870     x2 = 100;
2871     assert(x2 != cast(uint) 100);
2872     assert(x2.hook.calls == 3);
2873     x2 = -100;
2874 
2875     auto x3 = Checked!(uint, Hook2)(100u);
2876     assert(x3 != 100);
2877     x3 = uint.max;
2878     assert(x3 != -1);
2879 
2880     assert(x2 != x3);
2881 }
2882 
2883 // opCmp
2884 @nogc nothrow pure @safe unittest
2885 {
2886     Checked!(int, void) x;
2887     assert(x <= x);
2888     assert(x < 45);
2889     assert(x < 45u);
2890     assert(x > -45);
2891     assert(x < 44.2);
2892     assert(x > -44.2);
2893     assert(!(x < double.init));
2894     assert(!(x > double.init));
2895     assert(!(x <= double.init));
2896     assert(!(x >= double.init));
2897 
2898     static struct Hook1
2899     {
2900         uint calls;
2901         int hookOpCmp(Lhs, Rhs)(Lhs lhs, Rhs rhs)
2902         {
2903             ++calls;
2904             return 0;
2905         }
2906     }
2907     auto x1 = Checked!(int, Hook1)(42);
2908     assert(!(x1 < 43u));
2909     assert(!(43u < x1));
2910     assert(x1.hook.calls == 2);
2911 
2912     static struct Hook2
2913     {
2914         uint calls;
2915         int hookOpCmp(Lhs, Rhs)(Lhs lhs, Rhs rhs)
2916         {
2917             ++calls;
2918             return ProperCompare.hookOpCmp(lhs, rhs);
2919         }
2920     }
2921     auto x2 = Checked!(int, Hook2)(-42);
2922     assert(x2 < 43u);
2923     assert(43u > x2);
2924     assert(x2.hook.calls == 2);
2925     x2 = 42;
2926     assert(x2 > 41u);
2927 
2928     auto x3 = Checked!(uint, Hook2)(42u);
2929     assert(x3 > 41);
2930     assert(x3 > -41);
2931 }
2932 
2933 // opUnary
2934 @nogc nothrow pure @safe unittest
2935 {
2936     auto x = Checked!(int, void)(42);
2937     assert(x == +x);
2938     static assert(is(typeof(-x) == typeof(x)));
2939     assert(-x == Checked!(int, void)(-42));
2940     static assert(is(typeof(~x) == typeof(x)));
2941     assert(~x == Checked!(int, void)(~42));
2942     assert(++x == 43);
2943     assert(--x == 42);
2944 
2945     static struct Hook1
2946     {
2947         uint calls;
2948         auto hookOpUnary(string op, T)(T value) if (op == "-")
2949         {
2950             ++calls;
2951             return T(42);
2952         }
2953         auto hookOpUnary(string op, T)(T value) if (op == "~")
2954         {
2955             ++calls;
2956             return T(43);
2957         }
2958     }
2959     auto x1 = Checked!(int, Hook1)(100);
2960     assert(is(typeof(-x1) == typeof(x1)));
2961     assert(-x1 == Checked!(int, Hook1)(42));
2962     assert(is(typeof(~x1) == typeof(x1)));
2963     assert(~x1 == Checked!(int, Hook1)(43));
2964     assert(x1.hook.calls == 2);
2965 
2966     static struct Hook2
2967     {
2968         uint calls;
2969         auto hookOpUnary(string op, T)(ref T value) if (op == "++")
2970         {
2971             ++calls;
2972             --value;
2973         }
2974         auto hookOpUnary(string op, T)(ref T value) if (op == "--")
2975         {
2976             ++calls;
2977             ++value;
2978         }
2979     }
2980     auto x2 = Checked!(int, Hook2)(100);
2981     assert(++x2 == 99);
2982     assert(x2 == 99);
2983     assert(--x2 == 100);
2984     assert(x2 == 100);
2985 
2986     auto x3 = Checked!(int, CountOverflows)(int.max - 1);
2987     assert(++x3 == int.max);
2988     assert(x3.hook.calls == 0);
2989     assert(++x3 == int.min);
2990     assert(x3.hook.calls == 1);
2991     assert(-x3 == int.min);
2992     assert(x3.hook.calls == 2);
2993 
2994     x3 = int.min + 1;
2995     assert(--x3 == int.min);
2996     assert(x3.hook.calls == 2);
2997     assert(--x3 == int.max);
2998     assert(x3.hook.calls == 3);
2999 }
3000 
3001 //
3002 @nogc nothrow pure @safe unittest
3003 {
3004     Checked!(int, void) x;
3005     assert(x == x);
3006     assert(x == +x);
3007     assert(x == -x);
3008     ++x;
3009     assert(x == 1);
3010     x++;
3011     assert(x == 2);
3012 
3013     x = 42;
3014     assert(x == 42);
3015     const short _short = 43;
3016     x = _short;
3017     assert(x == _short);
3018     ushort _ushort = 44;
3019     x = _ushort;
3020     assert(x == _ushort);
3021     assert(x == 44.0);
3022     assert(x != 44.1);
3023     assert(x < 45);
3024     assert(x < 44.2);
3025     assert(x > -45);
3026     assert(x > -44.2);
3027 
3028     assert(cast(long) x == 44);
3029     assert(cast(short) x == 44);
3030 
3031     const Checked!(uint, void) y;
3032     assert(y <= y);
3033     assert(y == 0);
3034     assert(y < x);
3035     x = -1;
3036     assert(x > y);
3037 }